Learn Some Shell Script: For-loops

A lot of people I know seem to glory in their ignorance of the terminal. Which would be fine if they didn’t use it every day. Yes, bash is ugly. On the other hand, like many ugly things, it can be used as a brute tool to do a lot of mundane tasks.

Rather than making up some examples, here’s some of my recent shell history to illustrate this.

bash for-loops

The basic syntax is for i in X; do (some commands) "$i" ; done. i is the name of the loop variable, X is a whitespace-separated string which will be split into substrings according to that whitespace, and the do and done mark the beginning and end of the loop.

I dunno why, but the do cannot be its own command separated by semicolons, unlike the done. The semicolons are the same as in C and Java: they act as newlines.

The intermediate step is the interesting bit. But let’s get to the examples.

Examples From My Shell History

for i in {a..z};do
    html2text http://www.berkeley.edu/academics/dept/$i.shtml > $i.md

The {a..z} loops over letters in the obvious way. It works for numbers too.

I wanted to convert a bunch of html to Markdown. I used html2text to download and convert each one, and a for-loop to move them to the right spot. Since there was no whitespace in i’s values, I didn’t bother quoting $i.

for i in $(find . -name '*.md'); do echo $i; done

$(..) returns the output of the command inside as a string, which is then fed into the for-loop.

This recursively descends into the current directory, gets a list of all Markdown files, and prints out their names. I just wanted to read the names for some reason.

for i in {1..10}; do je $i;done

This creates journal files for the last 10 days. je is a function I wrote to create the file.

for i in ~/.{boto,irb,latexmk,msmtp,pry,gnuplot,goobook,ctags,xsh,node,es,calc,joe,mbsync,mime,less}; do mv ~/."$i" ~/.config/ ;done

The long comma-separated list between curly braces will expand into a bunch of strings with one element of the list in each one. I don’t recall if it’s a feature of the interactive shell or if it also works in shell scripts.

I started using ~/.config to store all my config files, and I used this long command to move all the directories into it. Easy cleanup.

for i in *; do echo $i;done
for i in *; do rm $i;done

The first command is a glob that prints out everything in the current directory. It can fail if the directory has a lot of files in it, since globs only expand to a few thousand elements. The second command deletes all of them. I used the first as a sanity check.

for i in $(seq 1 30); do echo $i;ssh -o ConnectTimeout=2 alok@192.168.1.$i; done

I needed to log in to a home computer I use for ML, but I didn’t want to hook it up to a monitor and get its up address. I also didn’t know about nmap or anything at the time. So I brute-forced it, and it worked after about 10 iterations.

for i in $(seq 1 5); do /usr/bin/time zsh -i -c exit; done

This was the poor man’s profiling of my shell startup time.

for i in *.py;do echo test_$i;done
for i in *.py;do touch test_$i;done

This created files for all my unit tests for a Python project.

Related Posts

How to Disable Disqus Ads on your Blog

Derivation of Reservoir Sampling

Fun with Python Iterators: Linked Lists Made Easy

Notes for November 11, 2018

Underrated Vim Option: undofile and undodir

Hot Take on Solo Travel: Starve

Alan Perlis

Book Notes: The Map of My Life by Goro Shimura


Way to remember the definition of local finiteness