Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compound logical tests in bash

Tags:

bash

testing

A question about bash ....

I wrote a little code to check whether a year is a leapyear. The relevant part looks like:

if [ $testnum4 -eq 0 -a $testnum100 -ne 0 ] || [ $testnum400 -eq 0 ]
then
  echo 0
fi

Well, this code actually works. My problem is that I'm still not quite sure what I was doing. Just to see what would happen, I have tried several variations on this: enclosing the entire test expression in square backets, using && where I have -a and -o where I have ||, etc. Nothing works except for the one expression that I wrote above. I stumbled on it, and I'm glad I did, but the next time I have to do something like this, I would like to know what I am doing.

So: Anyone have a quick explanation of how compound tests work in bash? or a pointer to a good resource on this topic?

Thanks.

Here's a code that failed:

let testnum4=$1%4
let testnum100=$1%100
let testnum400=$1%400

if [ $testnum4 -eq 0 && $testnum100 -ne 0 ] -o [ $testnum400 -eq 0 ]
then
  echo 0
fi
like image 668
bob.sacamento Avatar asked May 07 '12 17:05

bob.sacamento


2 Answers

Little note of interest: The square braces are actually a Unix command. See if there's a file called /bin/[ or /usr/bin/[ on your system. In fact, try this:

$ ls i-il /bin[ /bin/test
571541 -r-xr-xr-x  2 root  wheel  43120 Aug 17  2011 /bin/[
571541 -r-xr-xr-x  2 root  wheel  43120 Aug 17  2011 /bin/test

That first number is the i-node number. You see that /bin/[ and /bin/test are hard linked to each other. They're the same program.

I'll get back to the significance of this a bit later...


In BASH and other Bourne type shells, the if command merely runs the command, and if the command returns an exit code of 0, it executes the commands in the if clause. If the command does not return an exit code of 0, it skips the commands in the if clause and executes the commands in the else clause if it exists.

Thus, this is also valid:

if sleep 2
then
   echo "That was a good nap!"
fi

Your computer executes the sleep 2 command which (if the sleep command exists) will return a zero exit code and continue to echo That was a good nap!.

That's all the if command really does. It runs the given command and examines the exit code.


Now, back to the [...

You might want to look at the manpage for the test command. You can see that the test command seems to have many of the same type of tests as the [...] in the if statement. In fact, in the original Bourne shell (which BASH is derived from) these are the same command structure:

if test -f $my_file
then
    echo "File $my_file exists"
else
    echo "There is no file $my_file"
fi

if [ -f $my_file ]
then
    echo "File $my_file exists"
else
    echo "There is no file $my_file"
fi

In fact, in the original Bourne shell, you had to use the test command and not the square brackets.


So, back to your original question. The -a and -o are parameters for the test command (see the test manpage. For example, your if statement could have been written as this:

if test $testnum4 -eq 0 -a $testnum100 -ne 0 || test $testnum400 -eq 0
then
   echo 0
fi

However, the && and || are not test command parameters, so they can't be used inside the test command.

Note that [ is a built in command in BASH, so BASH does not execute /bin/[ as the original Bourne shell would have. However, it is still a command much like echo is a built in command in BASH although /bin/echo also still exists.

like image 78
David W. Avatar answered Sep 22 '22 15:09

David W.


The correct way to do integer comparisons in Bash is to use double parentheses:

if (( (testnum4 == 0 && testnum100 != 0) || testnum400 == 0 ))
like image 33
Dennis Williamson Avatar answered Sep 22 '22 15:09

Dennis Williamson