Or use [ -n ${FOO-} ] which will replace the unset variable with an empty string.
Or use [ -n ${FOO-} ] which will replace the unset variable with an empty string.
$ printf "<%s>\n" alpha ${beta} omega
<alpha>
<omega>
$ printf "<%s>\n" alpha ${beta-} omega
<alpha>
<omega>
The form ${var-} form is useful for safely evaluating a variable that might be unset, when "set -u" mode is in effect, and for whatever reason we cannot just fix the script so that the variable is set.The general form is "${FOO:-default}" where default can itself be another variable or whatever string you want.
I usually prefer to set default values at the top of a script though using this idiom:
: "${FOO:=bar}"
And when creating a local variable I'll immediately set it to an empty string if there's a chance it won't be assigned later: local foo=""With colon, tests variable for unset or empty. Without the colon tests only for unset. It's POSIX too:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...
If we have nothing in place of bar, they are effectively same.
I use this pattern all the time for variables that should be overridable by whoever is calling the script, ie. `FORCE="${FORCE-no}"`, overridable with `FORCE=yes foo.sh`. I'm not sure of any other way to do this.
(Yes, using getopt or other option parsing is better, but for my own scripts, env vars are just such a simpler way to pass options, and after 20 years of using bash/unix/linux I still can't write a getopt stanza from memory.)
: ${FORCE=no}
Also, it's not so difficult while getopts :hab:c o; do
case $o in
a) opt_a=x ;;
b) opt_b="$OPTARG" ;;
c) opt_c=x ;;
h) usage 0 ;;
?) usage 1 $o "$OPTARG" ;;
esac
done
shift $((OPTIND - 1))Heh, was that an attempt at sarcasm? That's super difficult to do from memory, at least for me. Maybe I'm not as good at memorizing things as you are.
There was definitely a wink component, but at the same time, I meant it.
Half the code is just case / esac syntax, which you can (i do) find plenty use for outside of getopts. getopts itself is… manageable. In the end, the code is such a strong pattern, it's basically a snippet. The lack of variation is what makes it kinda easy to remember. Or you could put it in your notes or a gist and copy/paste.
> That's super difficult to do from memory, at least for me. Maybe I'm not as good at memorizing things as you are.
Nah, my memory is shot. It's a matter of practice, and not being shy with man pages. Use it often enough, it's in memory; after a long hiatus, the details are a "/^\s*getopts \[" away (assuming your man pager is less).
I.e. user runs:
script --foo --bar=yes --no-xyzzy --uiuez"
the script then sets these existing variables: opt_foo=y
opt_foo_given=y
opt_bar=y
opt_bar_given=y
opt_xyzzy=
opt_xyzzy_given=y
However, the variable opt_uiuez doesn't exist and so it bails: script: no such option: --uiuez
With such a piece of code, all you do is define the options you support via assignments like "opt_foo=". You can give them default values this way. Include that piece of boiler-plate code. Done.To add a new option, just define the variable. Done.
Check its value wherever needed, and possibly the _given, if the code needs to know whether it's working with the default value, or an explicitly given value. That's it.
Bash has some odd behaviors and footguns but it can also be surprisingly reliable and versatile. Ive caught some seriously messed up stuff that I couldn't figure out otherwise, using set -x. Also shellcheck will forcefully hammer good practice into your head and catch a ton of hangups that are hard to know before making the mistakes.
also setting backup variables is a good idea like: pictures="${pics_dir:-$HOME/Pictures}"
You need double quotes.
Effectively strict mode for shell scripts.