Open Source

Try command-line looping for added efficiency

Using loops, you can take often-used commands that would normally be executed in sequence manually, and have them done automatically. Vincent Danen tells you what you need to know about getting started with loops.

Creating complex commands on the command-line can be challenging the first time, but is worth the invested effort. One aid that assists in creating complex commands is loops. Using loops, you can take often-used commands that would normally be executed in sequence manually, and have them done automatically. This is perhaps one of the most useful features in command-line usage other than perhaps piping output from one command as input to another, or redirecting output of commands.

The use of loops and if/else statements will be familiar to anyone who has done any kind of programming. And while these loops can be used in shell scripts, they can also be used on the command-line itself. For instance, often I find myself executing the same command over again, but with a slight variation in arguments. Instead of executing the command once, hitting the up arrow when it's completed, and changing one option, this can be automated to execute any number of commands in a row. It can also take advantage of using the output of other commands as input to constructing the loop.

For example, if you need to execute the same script with a varying argument, you could use:

$ for i in i586 x86_64; do seciurt 2008.1 $i foo.src.rpm; done

This command will call the seciurt script twice, the first time as seciurt 2008.1 i586 foo.src.rpm and the second time as seciurt 2008.1 x86_64 foo.src.rpm. In an instance where two arguments need to be changed in a logical format (i.e., calling seciurt to rebuild a src.rpm file for multiple distributions with the same two supported architectures), you would use:

$ for x in 2008.1 2008.0 2007.1; do for i in i586 x86_64; do seciurt $x $i foo-1.0-${x}.src.rpm; done; done

This calls seciurt six times; the first time would be as seciurt 2008.1 i586 foo-1.0-2008.1.src.rpm and the last time as seciurt 2007.1 x86_64 foo-1.0-2007.1.src.rpm. You will note that the variable $x is used two ways: $x and ${x}. The second is required to be called in that manner because there is no whitespace surrounding it and as such will match foo-1.0- instead of foo-1.0-2007.1.src.rpm, which can be seen by executing the following test:

$ touch foo1foo
$ x=1; ls foo$xfoo
ls: cannot access foo: No such file or directory
$ x=1; ls foo${x}foo

Remembering this "escaping" of variables can make for some very interesting CLI commands. The ability to nest loops within loops can also make things interesting with a little bit of creativity. Finally, you can use the output of other commands as input for the loop:

$ touch 1 2 3
$ for i in $(ls *); do echo $i; done

Here you can see the output of the ls command is used as arguments to the for loop.

Get the PDF version of this tip here.

Vincent Danen is the Security Team Manager for Mandriva and lives in Canada. He has been writing about and developing on Linux for over 10 years.

Delivered each Tuesday, TechRepublic's free Linux and Open Source newsletter provides tips, articles, and other resources to help you hone your Linux skills. Automatically sign up today!


Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.


Something I've tried to do in the past from the xp cli with no success is loop to the beginning of the command line, something like goto :0 or goto :%0 or somesuch.


#touch "this file" #touch "that file" #touch "another file" # for f in $(ls *); do echo $f;done this file that file another file ... not exactly an expected result. Why?


I was looking for a utility that would allow me to overwrite multiple files in a directory with one file (I know, it sound goofy, but it would take too long to explain). I was trying to download utilities, and also thinking about writing a quick little script, but with this article, there is no need. Thanks!


Couple of points: 1. It appears your running simple tests as the root user. This is not advisable. 2. There's no need to use "$(ls *)" as "*" will be expanded by the shell. In fact if "ls" is an alias for something else (such as "ls -l") you might be in for a surprise. 3. Using "*" instead of "$(ls *)" will probably give you the results you expect, because of the magic of shell expansion.


spaces in file names. Try renaming files using underscores in place of spaces ... ie, "that file" to that_file.

Editor's Picks