#+TITLE: TIL: set -u in bash #+KEYWORDS: cyborg #+LANGUAGE: en Trying a new /Today I learned/ section... Today, I learned about =set -u=, which will fail a bash script if we try to use a variable that's not defined. No more [[https://github.com/valvesoftware/steam-for-linux/issues/3671][deleting someone's files because a variable was not defined]]. Demo: #+BEGIN_SRC bash #!/bin/bash set -u GREETING=Hello #oups I misspelt that echo ${GREETINGS} world #+END_SRC If you run this: #+BEGIN_EXAMPLE $ ./script ./script: line 7: GREETINGS: unbound variable #+END_EXAMPLE That makes testing whether a variable is set slightly more complicated: #+BEGIN_SRC bash set -u if [[ -z ${GREETINGS} ]]; then # oups we never get here, the previous line fails with "unbound variable" GREETINGS=$( ....compute greetings ...) fi #+END_SRC Instead: #+BEGIN_SRC bash set -u if [[ ! -v GREETINGS ]]; then GREETINGS=$( ... compute greetings ... ) fi #+END_SRC Where [[https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html][=-v= is a Bash Conditional Expression]] described thusly: #+begin_quote - -v /varname/ :: True if the shell variable varname is set (has been assigned a value). #+end_quote I suppose the actually equivalent test would be: =\[\[ ! -v GREETINGS ]] || \[\[ -z ${GREETINGS} ]]=, to check whether the variable is not only set but also non-null. I've always used =set -e= and =set -o pipefail= in shell scripts. The first will exit the entire script if any command (more specifically: [[https://www.gnu.org/software/bash/manual/html_node/Pipelines.html][any pipeline]] fails). However by default, quoting the manual: the "exit status of a pipeline is the exit status of the last command in the pipeline", meaning the following succeeds: #+begin_example $ false | true $ echo $? 0 #+end_example But, if "=pipefail= is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully", so: #+begin_example $ set -o pipefail $ false | true $ echo $? 1 #+end_example TL;DR: Use =set -euo pipefail= at the top of your bash scripts. [[https://wizardzines.com/comics/bash-errors/][Julia Evans agrees]].