Compound logic tests in bash

Question about bash ....

I wrote a little code to check if the year is a leap. The relevant part is as follows:

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

Well, this code really works. My problem is that I'm still not quite sure what I am doing. Just to find out what will happen, I tried several options for this: including the entire test expression in square brackets using && , where I have -a and -o , where I have || etc. Nothing works except for one expression that I wrote above. I stumbled upon this, and I'm glad I did, but the next time I have to do something like this, I would like to know what I'm doing.

So: who has a quick explanation of how complex tests work in bash? or a pointer to a good resource on this topic?

Thanks.

Here the code 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 
+6
source share
4 answers

A quick note: square braces are actually a Unix command. See if your system has a file named /bin/[ or /usr/bin/[ . Actually try the following:

 $ 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 

This first number is the i-node number. You see that /bin/[ and /bin/test tightly coupled to each other. They are one and the same program.

I will return to the meaning of this a little later ...


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

Thus, this is also true:

 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 the echo. It was a good dream!

What the if command really does. It executes the given command and checks the exit code.


Now back to [ ...

You can see the man page for the test command. You can see that the test command seems to have the same test types as the [...] in the if statement. In fact, in the original Bourne shell (from which BASH is derived), this is 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 needed to use the test command, not the square brackets.


So, back to your original question. -a and -o are the parameters of the test command (see the test page. For example, your if could be written as follows:

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

However, && and || are not parameters of the test command, therefore they cannot be used inside the test command.

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

+16
source

The right way to do whole comparisons in Bash is to use double parentheses:

 if (( (testnum4 == 0 && testnum100 != 0) || testnum400 == 0 )) 
+3
source

Algorithm

Leap Year Validation Algorithm (in pseudo-code):

 if (YEAR modulo 400): LEAP_YEAR = true elseif (YEAR modulo 4) and not (YEAR modulo 100): LEAP_YEAR = true else LEAP_YEAR = false endif 

I would recommend not using smarter solutions and sticking to something more readable. See the Bash Starter Guide for a working example of a leap year test using nested if statements. It uses the deprecated arithmetic expression $[] , so just replace the arithmetic expressions with $(( )) and you will see how it should work.

How OPs Code Works

In the updated OPs code example, the values ​​are separate variables constructed from a positional parameter. Thus, modular arithmetic is performed for variables before they are calculated in conditional statements. After that, the if statement says:

 if (first_expression returns true) or (second_expression returns true): echo 0 endif 

Single-line brackets are just a built-in shell that performs the same actions as the test command, which evaluates the expression and returns the exit status, where zero is true. See help \[ or man test more details.

Creating a Better Mousetrap

While I and other posters explained why OPs code works, it seems that part of the goal of OPs is to run the entire leap year test in one conditional expression. This uses the Bash shell function to do this, and you can test the result by checking the exit status.

 # function returns exit status of arithmetic expression leap_year () { (( $1 % 400 == 0 || ($1 % 4 == 0 && $1 % 100 != 0) )) } 

You can check the function at the prompt:

 $ leap_year 2011; echo $? 1 $ leap_year 2012; echo $? 0 $ leap_year 2013; echo $? 1 

For more information about the arithmetic expression (( )) see Conditional Constructs in the Bash Reference Guide.

+1
source

In bash (in fact, most shells), if takes a command / set of commands and evaluates the then section if the last command returns true (zero exit code), otherwise it evaluates elif / else ( elif handled in a similar way to another).

Now about [[ . This is similar to the test command, but with a lot of testing coverage. However, it has the limitation that it can evaluate only one condition at a time, therefore it does not accept -o or -a . You can look [ as a true alias test . Regarding || and && , these are control statements that evaluate the next command if the previous command returns false (non-zero exit code) or true (zero exit code), respectively. Their use consists in combining teams (especially [[ ).

As for the above if, it executes 2 commands:

  • First: [ $testnum4 -eq 0 -a $testnum100 -ne 0 ]
  • Second: [ $testnum400 -eq 0 ]

If the first command succeeds, the if command executes then . If the first command is not executed, the second is executed. If the second is executed, then the body is executed, otherwise if does nothing.

Regarding a good bash resource, you can visit the bash wiki

0
source

Source: https://habr.com/ru/post/915109/


All Articles