command line, shell

Tests in bash

Tests are fundamental feature in bash scripting. However, tests in bash are not the same as test in some application – they do not check if your script works correctly, it’s a way to write an expressions that can be true or false.

$ [ 1 = 0 ]
$ echo $?
1  // false

$ [ 1 = 1 ]
$ echo $?
0  // true
$? variable is a special variable that gives you a number telling you result of the last-executed command. If it returned true, the number will (usually) be ‘0’. If it didn’t, the number will (usually) not be ‘0’.

Unlike in many programming languages, there is no difference in using single equal sign to double.

$ [ 1 = 1 ]
$ echo $?
0  // true

$ [ 1 == 1 ]
$ echo $?
0  // true

Space required

Hence [ and ] are builtins, the space is required after them. The [ is the separate command, and bash spacing is the way how bash determines where the one command ends and another begins.

$ [1 == 1]
bash: [1: command not found
$ echo $?
127

$ [ 1 == 1 ]
$ echo $?
0  // true

[ vs [[

The difference between them is very subtle, try below code to find out what it actually is.

$ unset DOESNOTEXIST

$ [ ${DOESNOTEXIST} = '' ]
bash: [: =: unary operator expected
$ echo $?
2   // misuse of builtin

$ [[ ${DOESNOTEXIST} = '' ]]
$ echo $?
0

First command with single [ evaluates command to [ = '' ], thus error is thrown. Command with [[ transforms empty variable to empty string [ '' = '' ] and the test is true.

In practice you should use [[ until there is a good reason not to. More on the differences between these two can be found here.

Unary operators

$ echo $PWD
/home/root
$ [[ -z $PWD ]]
$ echo $?
1  // false

-z returns true only if the argument is an empty string. Interestingly, this test will pass even we provide empty variable:

$ echo $FAKE
$ [[ -z $PWD ]]
$ echo $?
0  // TRUE!

Another most common unary operators are -a and -d.

$ touch file
$ [[ -a file ]]
$ echo $?
0 // TRUE = file exists

$ [[ -a second_file ]]
$ echo $?
1 // FALSE = file does not exist
$ mkdir folder
$ [[ -d folder ]]
$ echo $?
0 // TRUE = folder exists

$ [[ -a second_folder ]]
$ echo $?
1 // FALSE = folder does not exist

Binary operators

$ [[ 10 -lt 2 ]]    // less than
$ [[ 10 -gt 1 ]]    // greater than
$ [[ 10 -eq 1 ]]    // equals
$ [[ 10 -ne 1 ]]    // not equals

If statements

In the end, we are going to use tests very frequently in the if statements.

--- script.sh ---
#!/bin/bash
if [[ 10 -lt 5 ]]; then
   echo 'if block'
elif [[ 10 -gt 4 ]]; then
   echo 'elif block'
else
   echo 'else block'
fi

$ ./script.sh
elif block