if construction used very often in shell script, while not many users understand it's syntax correct. Manuals says:
if list; then list; [ elif list; then list; ] ... [ else list; ] fiWhere list is list of commands.
"if" is a conditional construction, a "then" list will be executed if "if" list was successfull.
Let's do some tests to understand how it works.
$ cd /tmp # A good place for junk files $ echo nana > a # Create file "a" contains "nana" word in it. $ rm -f b # Ensure that file "b" does not exist. $ grep -q nana a $ echo $? # Check exit status of previous command 0 $ grep -q mama a $ echo $? 1 $ grep -q mama b grep: b: No such file or directory $ echo $? 2Here "grep -q" command (-q - quiet, no output) return success status (0), when it found pattern and other values in other cases.
So, our first "if" costruction will be :
if grep -q nana a ; then echo "pattern found" fi
Common practice to use number of pipelined commands in testing constructions. Remember that only last command's exit status will checked. Would first command failed, this will be successfully ignored and this is probably not what you want. Example:
$ if hostname | grep -q localhost ; then echo "hostname not configured yet" fi $
My hostname is other than default "localhost", then this test did not print message exactly as it was expected. The second run contains typo in first command:
$ if hstname | grep -q localhost ; then echo "hostname not configured yet" fi bash: hstname: command not found $
The second run contains typo in first command, therefore it failed, nothing comes to grep command, then it did not find "localhost" pattern. In this case, nothing will be printed out even hostname does not configured yet.
OFF TOPIC: Try to use less commands in your scripts to be more efficient. Every command forks new process, allocates memory and manage all around it. When this happen within recursive loops, less commands can save a lot of time. Here is an example how to eliminate running unnessesary "hostname" command in Linux environment:
$ if grep -q localhost /proc/sys/kernel/hostname ; then echo "hostname not configured yet" fi $
"test" command obviously tests supplied condition and returns success value for true condition. Check "man test" for avaliable tests, they are very usefull. Example:
$ test 2 -le 5 $ echo $? 0 $ test 5 -le 2 $ echo $? 1
Historically "test" command aliased to [ symbol and was symbolic link to it, like:
$ ls -l /bin/[ -rwxr-xr-x. 1 root root 40888 May 19 18:18 /bin/[ -> testI saw they are separate command now in latest linuxes without real reason. The only difference in use [ command is to close condition with ] bracet. Same examples:
$ [ 2 -le 5 ] $ echo $? 0 $ [ 5 -le 2 ] $ echo $? 1Important NOTE: Now you know that they are not real bracets, but commands. You have to put spaces around braces, otherwice:
$ [2 -le 5] bash: [2: command not found $ [ 2 -le 5] bash: [: missing `]'
Combining these two command give us powerfull construction, you used to see:
if [ 2 -le 5 ] ; then echo "Correct" fi
Note:Please not forget that bracet is command when writing scripts. Check "man test" for avaliable costructions. Comparing numbers and strings is different.
HOSTNAME=localhost if [ $HOSTNAME = "localhost" ] ; then echo "hostname not configured yet" fi
Lets see what happen if variable will be zero (this happen very often when grepping and awking some inputs):
$ HOSTNAME="" $ if [ $HOSTNAME = "localhost" ] ; then echo "hostname not configured yet" fi bash: [: =: unary operator expected $
Still you remeber that bracet is really "test" command ? Zero length string is suggested as missing argument, that breaks ability to test. The script can skeep then an important part of code after this test fail. The solution is to prepend strings with common for both comparison sides value, like:
if [ 'x'$HOSTNAME = 'x'"localhost" ] ; then echo "hostname not configured yet" fi
Same solution can be used when integer number expected:
$ a=2 ; if [ '0'$a -lt '0'5 ] ; then echo "correct" ; fi correct $ a= ; if [ '0'$a -lt '0'5 ] ; then echo "correct" ; fi correct
Let's return to strings and check what will happen if our HOSTNAME variable gets multiline content in it:
$ HOSTNAME=$(echo -e "localhost\nAnother localhost line, grepped by mistake") $ if [ 'x'$HOSTNAME = 'x'"localhost" ] ; then echo "hostname not configured yet" fi bash: [: too many arguments
This is another very often mistake when writing scripts. Usually this happen when playing with file names wihtout thought they could have spaces and other special symbols in names. The solution is easy. EVERY time (except special cases) writes varuables surrounded by double quotes. The winning syntax will be:
if [ 'x'"$HOSTNAME" = 'x'"localhost" ] ; then echo "hostname not configured yet" fi
Don't you think we found the bullet-proof solution ? Then check this:
HOSTNAME=localhost if [ 'x'"$HOSTANME" = 'x'"localhost" ] ; then echo "hostname not configured yet" fi
In this case "if" construction checking undefined typo variable HOSTANME, when really defined HOSTNAME even not checked. Script will not warn you about, and you will sure it work correct. The good practice is check every few lines of code.
The command2 will run if command1 runs OK:
command1 opt opt opt && command2
Command3 will run if command2 fail:
command2 || command3
[ -d /mnt/cdrom ] && mkdir -p /mnt/cdrom # Create /mnt/cdrom directory if not exist cd /mnt/cdrom || exit 1 # Abort entire script if cannot "cd /mnt/cdrom" command1 || echo "command1 failed" # Print debug info