1

Having the following bash script:

#!/bin/bash

set -e

function foo() {
  # commands that might fails and I want to exit my script
  ...
  echo "result I need as output"
}

my_var=$(foo)

echo "I don't want this if there is an error inside foo"

Using set -e (in bash 4.4.19) does not seem to work with subshells i.e. the last echo command is still being executed). How can I write the code to make the script exit if any of the commands inside foo terminate with non-zero exit code.

I am using bash GNU bash, version 4.4.19(1)-release (x86_64-apple-darwin16.7.0) and the result of calling my script is (where the dots are replaced with an invalid command head -this:

$ ./my_script
head: illegal option -- t
usage: head [-n lines | -c bytes] [file ...]
I don't want this if there is an error inside foo
Gabriel Petrovay
  • 20,476
  • 22
  • 97
  • 168
  • because you didn't check the exit status of `head`; either `head .. || exit 1`, either enabling `-e` option inside subshell `my_var=$(set -e; foo)`, the first is better because explicit and easier for other to understand – Nahuel Fouilleul May 28 '18 at 13:51
  • And if `head` is actually `grep ... | head ... | awk ... | xargs ...` command, how can you rewrite these pipes? Using `(grep ... || exit 1) | (head ... || exit 1) | ...`? – Gabriel Petrovay May 28 '18 at 14:01
  • 1
    for a pipeline command by default exit status is the exit status of the last command of the pipeline, but can also be changed by `set -o pipefail`, note that `pipefail` is not cleared in subshell – Nahuel Fouilleul May 28 '18 at 14:08
  • Thanks! If you add this options to the answer with a corresponding comment, I think it would be useful as well. This is what I actually needed. – Gabriel Petrovay May 28 '18 at 14:11

1 Answers1

2

Changed from comments

the exit status of a pipeline command is the exit status of the last command, this can be changed using set -o pipefail, so that pipeline exit status will be <>0 is any command exit status is <>0.

First answer

as you used the -e option it is sufficient that the function returns a non 0 exit code for example return 1

in a more general case (without set -e), it can be better to use an explicit exit

my_var=$(foo) || exit 1

can be sufficient because error could be written on standard error (inherited) by subshell.

otherwise reading carefully manual can explain why it doesn't work as you expected

set -e

Exit immediately if [...] returns a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, [...].

This option applies to the shell environment and each subshell environment separately (see Command Execution Environment), and may cause subshells to exit before executing all the commands in the subshell.

[...]

And from Command Execution Environment

Subshells spawned to execute command substitutions inherit the value of the -e option from the parent shell. When not in POSIX mode, Bash clears the -e option in such subshells.

Community
  • 1
  • 1
Nahuel Fouilleul
  • 18,726
  • 2
  • 31
  • 36