1

How can I prevent command substitution in arguments from silently swallowing errors?

#!/bin/sh set -e echo "Evaluating $(false) should abort." echo "This line should not be executed." VAR="It should abort just like $(false) in assignments." echo "A workaround is using $VAR instead of command substitution." 
3
  • huh, I wasn't even aware of this difference; interesting. Commented Nov 27 at 12:00
  • What's an "error" in this context? A non-zero exit status in a subshell? Because false exiting with a non-zero exit status is surely not an error, and echo successfully outputting a string is not an error either. If the false is replaced by cat /tmp/nonexisting, then there is an error, and it is not swallowed (unless you throw away the error stream). However, echo still successfully outputs the string, and since it does, the shell does not terminate. Commented Nov 27 at 12:08
  • Also, POSIX has "The failure of any individual command in a multi-command pipeline, or of any subshell environments in which command substitution was performed during word expansion, shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered." Commented Nov 27 at 12:20

1 Answer 1

0

You can't really other than using your workaround, doing:

output=$(false) echo "Evaluating $output should abort." 

instead of:

echo "Evaluating $(false) should abort." 

Personally, I'd stay well clear off the errexit option (and if using it, I'd rather use set -o errexit than set -e), and do proper error handling:

#! /bin/sh - die() { [ "$#" -eq 0 ] || printf>&2 '%s\n' "$@" exit 1 } filter() { # don't consider it an error if there's no matching line grep "$@" || [ "$?" -eq 1 ] } pf() { set -o pipefail; } output=(pf; cmd | filter -v string) || die "cmd or filter failed" printf>&2 'I got this output: "%s"\n' "$output" # don't care if printf fails in this case as I want to carry on regardless cmd2 || die # ... 

Note that in:

set -o errexit a=$(false) b=$(true) printf '$%s: "%s"\n' a "$a" b "$b" 

None of the shells I tried exit on the a=$(false). You need to make sure you write it:

a=$(false); b=$(true) 

Or:

a=$(false) b=$(true) 

Or without errexit:

a=$(false) && b=$(true) || die 'Some message' 

Whether errexit causes exits in subshells or functions also depends on a number of factors, and on the shell and version thereof. Really, errexit is only usable in the most simple of scripts.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.