Compound logical tests in bash
bash, testing
Solution
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.
Problem
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 ```