shell-extras icon indicating copy to clipboard operation
shell-extras copied to clipboard

Should bash strict mode be recommended as good practice?

Open peterjc opened this issue 9 years ago • 14 comments

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.

peterjc avatar Nov 25 '15 17:11 peterjc

:+1: I think that's definitely appropriate for an intermediate audience.

kynan avatar Dec 01 '15 19:12 kynan

:+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.

lexnederbragt avatar Dec 02 '15 09:12 lexnederbragt

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.

gvwilson avatar Dec 02 '15 09:12 gvwilson

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'?

lexnederbragt avatar Dec 02 '15 09:12 lexnederbragt

@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.

kynan avatar Dec 12 '15 12:12 kynan

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...

lexnederbragt avatar Dec 12 '15 17:12 lexnederbragt

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.

kynan avatar Dec 20 '15 10:12 kynan

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 avatar Feb 20 '16 23:02 gdevenyi

@gdevenyi You can always append || true to any command to ignore a specific failure.

kynan avatar Feb 21 '16 13:02 kynan

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.

jttkim avatar Feb 22 '16 13:02 jttkim

Also consider shopt -s failglob

HaleTom avatar Feb 19 '17 07:02 HaleTom

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

(read more)

In this way you can safely source the script, if needed, without polluting your shell.

CristianCantoro avatar Dec 28 '17 15:12 CristianCantoro

@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 avatar Dec 29 '17 11:12 ssbarnea

@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

CristianCantoro avatar Dec 29 '17 18:12 CristianCantoro