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
cdsucceeds, the&& pwdexecutes and the current working directory name is printed. Unlesspwdfails (a rarity) the|| echo ...will not be executed. - If the
cdfails, the&& pwdwill 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
cdfails, the&& lsis skipped and the|| echo ...is executed. - If the
cdsucceeds, the&& lsis executed. - If the
lssucceeds, the|| echo ...is ignored. (so far so good) - BUT… if the
lsfails, 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
}