top | item 38397388

(no title)

mr-wendel | 2 years ago

Some tips of my own:

- It's almost always preferable to put `-u` (nounset) in your shebang to cause using undeclared variables to be an error. The only exception I typically run across is expansion of arrays using "${arr[@]}" syntax -- if the array is empty, this is considered unbound.

- You can use `-n` (noexec) as a poor-man's attempt at a dry-run, as this will prevent execution of commands.

- Also handy is `-e` (errexit), but you must take care to observe that essentially, this only causes "naked" commands that fail to cause an exit. Personally, I prefer to avoid this and append `|| fail "..."` to commands liberally.

discuss

order

augusto-moura|2 years ago

The problem with "${arr[@]}" only exists on bash 3 and before, since bash 4, [@] will never throw unbound variables even in cases where the variable is truly undefined. This is still a problem however, because macOS, to this day, still installs bash v3 by default and doesn't update it automatically (absolute madness, the last release of bash 3 it's from 20 years ago!).

In any case, you can workaround expanding empty arrays throwing unbound by using the ${var+alter} expansion

  echo "${arr+${arr[@]}}"

mmsc|2 years ago

  The problem with "${arr[@]}" only exists on bash 3 and before, since bash 4
4.4 fixed it:

  $ bash --version
  GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
  $ declare -A arr
  $ set -u
  $ "${arr[@]}"
  -bash: arr[@]: unbound variable

extraduder_ire|2 years ago

> macOS, to this day, still installs bash v3 by default and doesn't update it automatically (absolute madness, the last release of bash 3 it's from 20 years ago!).

GPL3, buddy. You can use brew to install a newer version and somewhat hide the old one.

bruce_one|2 years ago

One common suggestion is to not set Bash/shell options in the shebang because if someone runs the script with the interpreter explicitly (e.g. `bash my_script.sh`, rather than `./my_script.sh` (which, ime, is quite common as many people to seem to do it to avoid setting the execute bit, sometimes due to a lack of understanding ime)) then the options won't apply - which isn't fun...

Using `set` (e.g. `set -euo pipefail` as the first line after the shebang) is a common suggestion due to this, and similarly it helps with the `#!/usr/bin/env bash` case where the shebang can't handle additional arguments :-)

Calzifer|2 years ago

> to put `-u` (nounset) in your shebang

Any particular reason why in the shebang instead of set -u?

> The only exception I typically run across is expansion of arrays using "${arr[@]}" syntax

In Bash? Works for me. Edit: another comment mentions it as well. Seem to behave better in newer versions of Bash and only problematic in <= 4.3 https://news.ycombinator.com/item?id=38397241

  $ bash -uc 'unset x; echo "=> ${x[@]}"'
  =>
  $ bash -uc 'x=(); echo "=> ${x[@]}"'
  =>
  $ bash -uc 'x=(); echo "=> ${x[0]}"'
  bash: x[0]: unbound variable
Zsh does not like the first example but both should support:

  $ bash -uc 'unset x; echo "=> ${x[@]:-null}"'
  => null

> Also handy is `-e` (errexit),

It is unfortunately very confusion with functions. Made me like it less over the years.

mr-wendel|2 years ago

Using the shebang just helps highlight the fact that the rule is in use globally, but otherwise has no advantage to using `set -u`.

The clarifications on `-u` and arrays are useful. I'm definitely used to assuming newer (... non-ancient?) versions of Bash are what is available.

anamexis|2 years ago

Regarding the `-e` issue, this is well handled by `-o pipefail`, which as of last year is part of POSIX.

extraduder_ire|2 years ago

I find that I usually have to use traps when I put use options to terminate the script early, mostly when I have some files to clean up.

"trap" is a great scripting feature that doesn't get talked about enough, IME.