TL;DR
You should write the following at the beginning of every script:
set -euo pipefail shopt -s lastpipe
Long version
Bash is a common used script language, if it comes to write some shell scripts for Unix. To make your scripts safer for common mistakes you should set some options of Bash in your script to prevent unexpected behavior and to enable some stricter rules.
set -e
If you set this option, Bash will immediately stop execution of your script if an error occurs.
The script:
#!/bin/bash unknowncommand echo "my second line"
will output with an return code of 0:
./test.sh: line 3: unknowncommand: command not found my second output
If you add set -e
at the beginning:
#!/bin/bash set -e unknowncommand echo "my second line"
you will get the following output with an return code of 127.
./test.sh: line 5: unknowncommand: command not found
So you can trust that if a command like mkdir
or cd
for example will fail, your script will stop.
If you want to allow a command to fail, you have to add || true
, for example:
#!/bin/bash set -e unknowncommand || true echo "my second line"
set -u
This option will prevent you from using undefined variables.
The script:
#!/bin/bash echo "Hello $NAME"
will output:
Hello
Bash handle undefined variables as empty variables per default. If you add set -u
at the beginning of your script:
#!/bin/bash set -u echo "Hello $NAME"
you will get the following output:
./test.sh: line 5: NAME: unbound variable
If you are not sure, if the variable exists, you can use the syntax for default values:
#!/bin/bash set -u echo "Hello ${NAME:-World}"
set -o pipefail
If you are using pipes, only the last command will define the result of the whole pipe. So if you run this script:
#!/bin/bash set -eu unknowncommand | echo "other command" echo "after command"
you will get the following output:
./test.sh: line 5: unknowncommand: command not found other command after command
If you add set -o pipefail
at the beginning of your script:
#!/bin/bash set -euo pipefail unknowncommand | echo "other command" echo "after command"
the script will stop after the failed pipe and you get the output:
./test.sh: line 5: unknowncommand: command not found
shopt -s lastpipe
If you run a script in a pipe and want to set variables, this won’t work, because your command is run in a sub shell and then you cannot change variables.
The following script:
#!/bin/bash set -euo pipefail VAR=Hello echo "some" | { while read -r file ; do VAR=World; done } echo $VAR
would output:
Hello
If you add shopt -s lastpipe
at the beginning of your script:
#!/bin/bash set -euo pipefail shopt -s lastpipe VAR=Hello echo "some" | { while read -r file ; do VAR=World; done } echo $VAR
the script would output:
World