Bash AliasingBash ArithmeticBash ArraysBash Associative arraysBash Avoiding date using printfBash Brace ExpansionBash Case statementBash CGI ScriptsBash Chain of commands and operationsBash Change shellBash co-processesBash Color script output (cross-platform)Bash Conditional ExpressionsBash Control StructuresBash Copying (cp)Bash Creating directoriesBash Customizing PS1Bash Cut CommandBash DebuggingBash Decoding URLBash Design PatternsBash File execution sequenceBash File Transfer using scpBash FindBash FunctionsBash getopts : smart positional-parameter parsingBash global and local variablesBash GrepBash Handling the system promptBash Here documents and here stringsBash history substitutionsBash Internal variablesBash IntroductionBash Job ControlBash Jobs and ProcessesBash Jobs at specific timesBash Keyboard shortcutsBash Listing FilesBash Managing PATH environment variableBash MathBash NamespaceBash Navigating directoriesBash on Windows 10Bash ParallelBash Parameter ExpansionBash Pattern matching and regular expressionsBash PipelinesBash PitfallsBash Process substitutionBash Programmable completionBash QuotingBash Read a file (data stream, variable) line-by-line (and/or field-by-field)Bash RedirectionBash ScopingBash Script shebangBash Scripting with ParametersBash Select keywordBash Sleep utilityBash SourcingBash Splitting FilesBash straceBash the cut commandBash true, false and : commandsBash Type of ShellsBash Typing variablesBash Using catBash Using sortBash Using trap to react to signals and system eventsBash when to use evalBash Word splittingNetworking with Bash

Bash Conditional Expressions

From WikiOD

Syntax[edit | edit source]

Remarks[edit | edit source]

The …  syntax surrounds bash built-in conditional expressions. Note that spaces are required on either side of the brackets.

Conditional expressions can use unary and binary operators to test properties of strings, integers and files. They can also use the logical operators &&, || and !.

File type tests[edit | edit source]

The -e conditional operator tests whether a file exists (including all file types: directories, etc.).

if [[ -e $filename ]]; then
  echo "$filename exists"

There are tests for specific file types as well.

if [[ -f $filename ]]; then
  echo "$filename is a regular file"
elif [[ -d $filename ]]; then
  echo "$filename is a directory"
elif [[ -p $filename ]]; then
  echo "$filename is a named pipe"
elif [[ -S $filename ]]; then
  echo "$filename is a named socket"
elif [[ -b $filename ]]; then
  echo "$filename is a block device"
elif [[ -c $filename ]]; then
  echo "$filename is a character device"
if [[ -L $filename ]]; then
  echo "$filename is a symbolic link (to any file type)"

For a symbolic link, apart from -L, these tests apply to the target, and return false for a broken link.

if [[ -L $filename || -e $filename ]]; then
  echo "$filename exists (but may be a broken symbolic link)"

if [[ -L $filename && ! -e $filename ]]; then
  echo "$filename is a broken symbolic link"

String comparison and matching[edit | edit source]

String comparison uses the == operator between quoted strings. The != operator negates the comparison.

if [[ "$string1" == "$string2" ]]; then
  echo "\$string1 and \$string2 are identical"
if [[ "$string1" != "$string2" ]]; then
  echo "\$string1 and \$string2 are not identical"

If the right-hand side is not quoted then it is a wildcard pattern that $string1 is matched against.

if [[ "$string" == $pattern1 ]]; then
  # the test is true
  echo "The string $string matches the pattern $pattern"
if [[ "$string" != $pattern2 ]]; then
  # the test is false
  echo "The string $string does not match the pattern $pattern"

The < and > operators compare the strings in lexicographic order (there are no less-or-equal or greater-or-equal operators for strings).

There are unary tests for the empty string.

if [[ -n "$string" ]]; then
  echo "$string is non-empty"
if [[ -z "${string// }" ]]; then
  echo "$string is empty or contains only spaces"
if [[ -z "$string" ]]; then
  echo "$string is empty"

Above, the -z check may mean $string is unset, or it is set to an empty string. To distinguish between empty and unset, use:

if [[ -n "${string+x}" ]]; then
    echo "$string is set, possibly to the empty string"
if [[ -n "${string-x}" ]]; then
    echo "$string is either unset or set to a non-empty string"
if [[ -z "${string+x}" ]]; then
    echo "$string is unset"
if [[ -z "${string-x}" ]]; then
    echo "$string is set to an empty string"

where x is arbitrary. Or in table form:

            $string is: | unset | empty | non-empty |
| [[ -z ${string} ]]    | true  | true  | false     |
| [[ -z ${string+x} ]]  | true  | false | false     |
| [[ -z ${string-x} ]]  | false | true  | false     |
| [[ -n ${string} ]]    | false | false | true      |
| [[ -n ${string+x} ]]  | false | true  | true      |
| [[ -n ${string-x} ]]  | true  | false | true      |

Alternatively, the state can be checked in a case statement:

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank

Where [:blank:] is locale specific horizontal spacing characters (tab, space, etc).

Test on exit status of a command[edit | edit source]

Exit status 0: success

Exit status other than 0: failure

To test on the exit status of a command:

if command;then
    echo 'success'
    echo 'failure'

File comparison[edit | edit source]

if [[ $file1 -ef $file2 ]]; then
  echo "$file1 and $file2 are the same file"

“Sameme file” means that modifying one of the files in place affects the other. Two files can be the same even if they have different names, for example if they are hard links, or if they are symbolic links with the same target, or if one is a symbolic link pointing to the other.

If two files have the same content, but they are distinct files (so that modifying one does not affect the other), then -ef reports them as different. If you want to compare two files byte by byte, use the cmp utility.

if cmp -s -- "$file1" "$file2"; then
  echo "$file1 and $file2 have identical contents"
  echo "$file1 and $file2 differ"

To produce a human-readable list of differences between text files, use the diff utility.

if diff -u "$file1" "$file2"; then
  echo "$file1 and $file2 have identical contents"
  : # the differences between the files have been listed

File access tests[edit | edit source]

if [[ -r $filename ]]; then
  echo "$filename is a readable file"
if [[ -w $filename ]]; then
  echo "$filename is a writable file"
if [[ -x $filename ]]; then
  echo "$filename is an executable file"

These tests take permissions and ownership into account to determine whether the script (or programs launched from the script) can access the file.

Beware of race conditions (TOCTOU): just because the test succeeds now doesn't mean that it's still valid on the next line. It's usually better to try to access a file, and handle the error, rather than test first and then have to handle the error anyway in case the file has changed in the meantime.

Numerical comparisons[edit | edit source]

Numerical comparisons use the -eq operators and friends

if [[ $num1 -eq $num2 ]]; then
  echo "$num1 == $num2"
if [[ $num1 -le $num2 ]]; then
  echo "$num1 <= $num2"

There are six numeric operators:

  • -eq equal
  • -ne not equal
  • -le less or equal
  • -lt less than
  • -ge greater or equal
  • -gt greater than

Note that the < and > operators inside …  compare strings, not numbers.

if [[ 9 -lt 10 ]]; then
  echo "9 is before 10 in numeric order"
if [[ 9 > 10 ]]; then
  echo "9 is after 10 in lexicographic order"

The two sides must be numbers written in decimal (or in octal with a leading zero). Alternatively, use the ((…)) arithmetic expression syntax, which performs integer calculations in a C/Java/…-like syntax.

if ((2*x == 4)); then
  echo "2 times 2 is 4"
((x += 1))
echo "2 plus 1 is $x"

One liner test[edit | edit source]

You can do things like this:

[[ $s = 'something' ]] && echo 'matched' || echo "didn't match"
[[ $s == 'something' ]] && echo 'matched' || echo "didn't match"
[[ $s != 'something' ]] && echo "didn't match" || echo "matched"
[[ $s -eq 10 ]] && echo 'equal' || echo "not equal"
(( $s == 10 )) && echo 'equal' || echo 'not equal'

One liner test for exit status:

command && echo 'exited with 0' || echo 'non 0 exit'
cmd && cmd1 && echo 'previous cmds were successful' || echo 'one of them failed'
cmd || cmd1 #If cmd fails try cmd1