shell-extras
shell-extras copied to clipboard
Should bash strict mode be recommended as good practice?
Should bash strict mode be recommended as good practice? See http://redsymbol.net/articles/unofficial-bash-strict-mode/ which uses:
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
Personally I use set -e
most heavily, and generally just use the one line set -euo pipefail
after the hash-bang.
Originally raised as https://github.com/swcarpentry/shell-novice/issues/251 but we agreed it was too advanced for the shell novice lesson.
:+1: I think that's definitely appropriate for an intermediate audience.
:+1: from me too. Makes me wonder whether there could be a shell-scripting lesson here as well, including this, using functions, if...then statements.
I'm -0.5 on a shell scripting lesson: by the time you need conditionals and functions, you should have switched to a language with proper data structures.
I completely agree, and I keep saying this to many people on our lab, but getting them to give up the (false) comfort of their bash skills and scripts, and take a course in python/R/whatever that gets them to the same level of comfort, is not something I am very successful with (I need more power ;-) ).
Maybe a lesson on 'if you (would like to) do this in a shell script, the equivalent python script would look like this'?
@gvwilson @lexnederbragt Agree with you in principle, however there are many use cases where shell scripts are still the better choice: for running a series of commands (executables) you should really not use Python (in particular not os.system
!) or another high level language. For that a bash script with set -euo pipefail
is the way to go.
Hmmm... I am more and more convinced that a Makefile is the better way to go in such a case. But that has another set of drawbacks: not easy to learn, less transparent what is being run...
Sure, as soon as it gets slightly more complex and you have interdependencies a Makefile is definitely superior. But there is that narrow use case for bash scripts and for that imho strict mode is very important.
If we recommend strict mode, we should note it could break some workflows.
I recently ran into an issue where I was some text manipulation in a subshell that can fail which was okay for the toolchain, but when I added strict the toolchain would exit if that subshell call failed.
@gvwilson When you work on a toolchain that exclusively relies on manipulating files and chaining together other command line tools, what do you suggest if not bash?
@gdevenyi You can always append || true
to any command to ignore a specific failure.
Getting back to @peterjc's initial comment of using set -e
, it seems to me that discussing the elements of the "unofficial strict mode" individually may make sense. Personally I
- recommend
-e
and-o pipefail
unreservedly, - am generally in favour of
-u
as well, but would expect that this may result in learners finding that numerous examples / tutorials / code snippets don't seem to work for them, so people need to be prepared a bit, - am not so sure about the
IFS
setting -- interfering with the scan / parse process like that doesn't seem right to me, although I have sympathy for the motivation of trying to write scripts that can deal with file names containing spaces without running into trouble due to tokenising these.
Also consider shopt -s failglob
I would recommend this version:
#!/usr/bin/env bash
SOURCED=false && [ "$0" = "$BASH_SOURCE" ] || SOURCED=true
if ! $SOURCED; then
set -euo pipefail
IFS=$'\n\t'
fi
In this way you can safely source the script, if needed, without polluting your shell.
@CristianCantoro Interesting idea but I see two problems with it: it may fail if run with non bash shell or if you already had set -u
enabled before calling it, as BASH_SOURCE
would not be defined. I guess is going into the right direction anyway.
@ssbarnea to be fair this is the bash strict mode, so I would say that using bash
is implied :wink: (I use zsh
as my shell, but I write almost all of my shell scripts in bash).
However, you are absolutely right, I didn't notice this issue it until because it manifests itself only if you source the script directly from a non-bash shell and you have set -u
either before in the script or in your shell.
If you are using bash
, BASH_SOURCE
is a variable, so it is available and the script does not fail even with set -u
.
This would be a fix:
#!/usr/bin/env bash
SOURCED=true
if [ ! -z "${BASH_SOURCE+x}" ]; then
SOURCED=false && [ "$0" = "$BASH_SOURCE" ] || SOURCED=true
fi
if ! $SOURCED; then
set -euo pipefail
IFS=$'\n\t'
fi
A little bit more verbose, and it still does not support other shells, though