Bash Shell Scripting/Conditional Expressions

Very often, we want to run a certain command only if a certain condition is met. For example, we might want to run the command  ("copy the file   to location  ") if, and only if,   exists. We can do that like this:

The above uses two built-in commands:


 * The construction  returns an exit status of zero (success) if   is true, and a nonzero exit status (failure) if   is false. In our case,   is , which is true if and only if there exists a file named.
 * The construction  < first runs ; if that completes successfully (that is, if its exit status is zero), then it goes on to run.

In other words, the above is equivalent to this:

except that it is more clear (and more flexible, in ways that we will see shortly).

In general, Bash treats a successful exit status (zero) as meaning "true" and a failed exit status (nonzero) as meaning "false", and vice versa. For example, the built-in command  always "succeeds" (returns zero), and the built-in command   always "fails" (returns one).

statements
statements are more flexible than what we saw above; we can actually specify multiple commands to run if the test-command succeeds, and in addition, we can use an  clause to specify one or more commands to run instead if the test-command fails:

The commands can even include other  statements; that is, one   statement can be "nested" inside another. In this example, an  statement is nested inside another   statement's   clause:

This particular pattern — an  clause that contains exactly one   statement, representing a fallback-test — is so common that Bash provides a convenient shorthand notation for it, using   ("else-if") clauses. The above example can be written this way:

A single  statement can have any number of   clauses, representing any number of fallback conditions.

Lastly, sometimes we want to run a command if a condition is false, without there being any corresponding command to run if the condition is true. For this we can use the built-in  operator, which precedes a command; when the command returns zero (success or "true"), the   operator changes returns a nonzero value (failure or "false"), and vice versa. For example, the following statement will copy  to   unless   already exists:

All those examples above are examples using the  expressions. Actually  just runs everything in   when the command in the statement returns 0: So the behavior of  is quite like the logical 'and'   and 'or'   in some ways: Always notice that misuse of those logical operands may lead to errors. In the case above, everything was fine because plain  is almost always successful.

Conditional expressions
In addition to the  condition used above, which is true if   exists, there are quite a few kinds of conditions supported by Bash's   notation. Five of the most commonly used are:


 * : True if  exists and is a directory.
 * : True if  exists and is a regular file.
 * : True if  exists, whatever it is.
 * : True if  matches  . (  has the same form as a pattern in filename expansion; for example, unquoted   means "zero or more characters".)
 * : True if  does not match.
 * : True if  contains Posix extended regular expression  . See Regular_Expressions/POSIX-Extended_Regular_Expressions for more information.

In the last three types of tests, the value on the left is usually a variable expansion; for example,  returns a successful exit status if the variable named   contains the value.

The above conditions just scratch the surface; there are many more conditions that examine files, a few more conditions that examine strings, several conditions for examining integer values, and a few other conditions that don't belong to any of these groups.

One common use for equality tests is to see if the first argument to a script is a special option. For example, consider our  statement above that tries to copy   or   to. The above version is very "verbose": it generates a lot of output. Usually we don't want a script to generate quite so much output; but we may want users to be able to request the output, for example by passing in  as the first argument. The following script is equivalent to the above  statements, but it only prints output if the first argument is  :

Later, when we learn about shell functions, we will find a more compact way to express this. (In fact, even with what we already know, there is a more compact way to express this: rather than setting  to   or , we can set   to   or  , where the colon   is a Bash built-in command that does nothing. We can then replace all uses of   with  . A command such as   would then become  , printing  , if verbose-mode is turned on, but would become  , doing nothing, if verbose-mode is turned off. However, that approach might be more confusing than is really worthwhile for such a simple purpose.)

Combining conditions
To combine multiple conditions with "and" or "or", or to invert a condition with "not", we can use the general Bash notations we've already seen. Consider this example:

The test-command  uses the   and   operators that we saw above that work based on exit status. is "successful" if  is true, which means that   will only run   if   exists. Furthermore,  inverts the exit status of , so that   is "successful" if and only if   doesn't exist. The end result is that  is "successful" — "true" — if and only if   does exist and   does not exist.

The construction  actually has built-in internal support for these operators, such that we can also write the above this way:

but the general-purpose notations are often more clear; and of course, they can be used with any test-command, not just the  construction.

Notes on readability
The  statements in the above examples are formatted to make them easy for humans to read and understand. This is important, not only for examples in a book, but also for scripts in the real world. Specifically, the above examples follow these conventions:


 * The commands within an  statement are indented by a consistent amount (by two spaces, as it happens). This indentation is irrelevant to Bash — it ignores whitespace at the beginning of a line — but is very important to human programmers. Without it, it is hard to see where an   statement begins and ends, or even to see that there is an   statement. Consistent indentation becomes even more important when there are   statements nested within   statements (or other control structures, of various kinds that we will see).
 * The semicolon character  is used before  . This is a special operator for separating commands; it is mostly equivalent to a line-break, though there are some differences (for example, a comment always runs from   to the end of a line, never from   to  ). We could write   at the beginning of a new line, and that is perfectly fine, but it's good for a single script to be consistent one way or the other; using a single, consistent appearance for ordinary constructs makes it easier to notice unusual constructs. In the real world, programmers usually put   at the end of the   or   line, so we have followed that convention here.
 * A newline is used after  and after  . These newlines are optional — they need not be (and cannot be) replaced with semicolons — but they promote readability by visually accentuating the structure of the   statement.
 * Regular commands are separated by newlines, never semicolons. This is a general convention, not specific to  statements. Putting each command on its own line makes it easier for someone to "skim" the script and see roughly what it is doing.

These exact conventions are not particularly important, but it is good to follow consistent and readable conventions for formatting your code. When a fellow programmer looks at your code — or when you look at your code two months after writing it — inconsistent or illogical formatting can make it very difficult to understand what is going on.