Wednesday, April 30, 2008

Bash For Loops

I use bash and zsh quite a bit in system admin tasks. One thing I find myself needing to do repeatedly is run a command in a loop a given number of times. I'm very well acquainted with cron, but there are often instances where I prefer to run a bash loop inside of a screen session while logging to a file. This gives a lot more control over bleeding edge scripts, and it's easier to "check in" and see what's going on. Here are some simple ways to loop with bash:

# C style for loop
for ((i=0; i<=100; i+=1)); do
# some command
sleep 1
done

# quick loop

for I in 1 2 3; do
echo $I
sleep 1
done

# infinite loop

while [ 1 ]; do
# some repeated command
sleep 60
done

# file loop

for I in `ls *.txt`; do
echo "file $I"
done

4 comments:

Steve Laniel said...

Couple things:

1) I'm not sure if it's more portable, but I've always preferred `seq M N` when N-M is large, instead of the ((...)) form.

2) I've seen a lot of people use the "for i in `ls *.txt`" form, and I've not quite understood the appeal: "for i in *.txt" is identical, I'm told more portable, and shorter.

Dag said...

The zsh shell also allows this shortened syntax for single-command loops:

for f (*.txt) echo "file $f"

Anonymous said...

find . -maxdepth 5 -type f -iname "*.txt" -exec echo {} \;

This does same job like file loop in blog but
* ignores case of filename (-iname)
* recursively with maximum depth of 5 sub dirs (-maxdepth)
* listing only regular files (-type) (f=files, d=dirs)

Travis Whitton said...

@steve:

Didn't know about using seq, but that's a nice trick. Backticking ls to glob expand files is indeed unnecessary. Although, there are times when a backtick in a loop context is helpful to operate on the output of an external command, it's overkill in this case.

@dag:

I didn't know that syntax. Thanks for the tip.

@anon:

Yup, find / exec is a really nice combo. Thanks for mentioning it.