I grew up scripting with the C shell (csh), and I'm still more comfortable with it than any other shells. However, for really complicated scripts I find that BASH (the Bourne-again shell) is more flexible. The problem is that I don't really know how to use it all that well.
I could buy a book, I suppose, but that costs money. And anyway, much of what I want is already on the web. For example, we have
- The BASH Reference Manual
- The BASH Guide for Beginners
- The Advanced BASH Scripting Guide
- Handling Command Line Arguments
I've used all of these, but the only way to really remember something is to build an example, and refer back to it as needed. So here, presented for your consideration, are a couple of examples.
The first one just counts. It's basically here to show how to do math with variables in BASH:
#! /bin/bash typeset -i count count=$1 echo $count while (( $count < $2 )) do count=$count+1 echo $count done
Actually, that one is adapted from the Korn shell (ksh), but it works in bash. A purer bash form would be
#! /bin/bash let count=$1 echo $count while (( $count < $2 )) do let count=$count+1 echo $count done
Either way, if we call the file count, then it counts from the first argument up to the second:
./count 5 10 5 6 7 8 9 10
On to another program. I often have programs that recompute a certain quantity repeatedly, say the total energy in a Density Functional Theory run. For example,
$ grep "TOTAL ENERGY=" INFO TOTAL ENERGY= -551.5802151120 TOTAL ENERGY= -551.5798689965 TOTAL ENERGY= -551.5810124810 TOTAL ENERGY= -551.5809217653 TOTAL ENERGY= -551.5796453493 TOTAL ENERGY= -551.5797608209 TOTAL ENERGY= -551.5806860076 TOTAL ENERGY= -551.5804690663 TOTAL ENERGY= -551.5793575483 TOTAL ENERGY= -551.5794583810
where I only want the last line. For a single file, I could get that with
$ grep "TOTAL ENERGY=" INFO | tail -1 TOTAL ENERGY= -551.5794583810
but I want to look at multiple files, make a note of the file I'm looking at, and extract the last entry. So I wrote this little script, which I call lastgrep. Among other things, it shows you how to use the shift command to delete elements from the calling string.
#! /bin/bash # usage # lastgrep string file1 file2 file3 file4 # finds the last occurrence of string in each of the files # listed in the argument list. If string contains white space # it should be quoted string=$1 # Pop the first argument (string) off the stack. The # remaining arguments should be files shift for file in $@ do echo -n $file # Put quotes around $string to keep white space in place grep "$string" $file | tail -1 done
So if I have a set of files INFO in directories a100, a200, a300, I might do something like this:
$ lastgrep "TOTAL ENERGY=" a*/INFO a100/INFO TOTAL ENERGY= -551.7810244761 a200/INFO TOTAL ENERGY= -551.6774784178 a300/INFO TOTAL ENERGY= -551.6192819148
What this doesn't have is a way of passing arguments to grep. That's for another time.