Conditional execution of command lists
suggest changeHow to use conditional execution of command lists
Any builtin command, expression, or function, as well as any external command or script can be executed conditionally using the &&
(and) and ||
(or) operators.
For example, this will only print the current directory if the cd
command was successful.
cd my_directory && pwd
Likewise, this will exit if the cd
command fails, preventing catastrophe:
cd my_directory || exit
rm -rf *
When combining multiple statements in this manner, it’s important to remember that (unlike many C-style languages) these operators have no precedence and are left-associative.
Thus, this statement will work as expected…
cd my_directory && pwd || echo "No such directory"
- If the
cd
succeeds, the&& pwd
executes and the current working directory name is printed. Unlesspwd
fails (a rarity) the|| echo ...
will not be executed. - If the
cd
fails, the&& pwd
will be skipped and the|| echo ...
will run.
But this will not (if you’re thinking if...then...else
)…
cd my_directory && ls || echo "No such directory"
- If the
cd
fails, the&& ls
is skipped and the|| echo ...
is executed. - If the
cd
succeeds, the&& ls
is executed. - If the
ls
succeeds, the|| echo ...
is ignored. (so far so good) - BUT… if the
ls
fails, the|| echo ...
will also be executed.
It is the ls, not the cd, that is the previous command.
Why use conditional execution of command lists
Conditional execution is a hair faster than if...then
but its main advantage is allowing functions and scripts to exit early, or “short circuit”.
Unlike many languages like C
where memory is explicitly allocated for structs and variables and such (and thus must be deallocated), bash
handles this under the covers. In most cases, we don’t have to clean up anything before leaving the function. A return
statement will deallocate everything local to the function and pickup execution at the return address on the stack.
Returning from functions or exiting scripts as soon as possible can thus significantly improve performance and reduce system load by avoiding the unnecessary execution of code. For example…
my_function () {
### ALWAYS CHECK THE RETURN CODE
# one argument required. "" evaluates to false(1)
[[ "$1" ]] || return 1
# work with the argument. exit on failure
do_something_with "$1" || return 1
do_something_else || return 1
# Success! no failures detected, or we wouldn't be here
return 0
}