1

Given the following contrived code:

#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit

echo 'before'

mapfile -t tuples < <(exit 1)
# ^ what option do I need to enable so the error exit code from this is not ignored

echo 'after'

Which produces:

before
after

Is there a set or shopt option that can be turned on such that <(exit 1) will cause the caller to inherit the failure, and thus preventing after from being executed? Such as what inherit_errexit and pipefail do in other contexts.

balupton
  • 47,113
  • 32
  • 131
  • 182
  • The problem is that the shell doesn't wait for a process run with process substitution to exit before executing the next command. So nothing that depends on its exit status can be used. – Barmar Apr 28 '22 at 14:41
  • This allows you to run programs that never exit in a process substitution, e.g. `head -10 < <(tail -f filename)` – Barmar Apr 28 '22 at 14:42
  • @Barmar bummer, yeah seems (1) `<(...)` is shorthand for create a file which output is done via `...`, with the implementation discarding any errors https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution (2) that `mapfile` because of this also only emits errors with mapfile https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtins – balupton Apr 28 '22 at 14:56
  • @Barmar is there an alternative syntax then that is recommended for when I do want to respect the error code? Or is the only option check if the mapfile variable is empty after the fact? – balupton Apr 28 '22 at 14:57
  • For those wondering, `mapfile -t tuples <<< "$(exit 1)"` also produces the same ignored behaviour. – balupton Apr 28 '22 at 14:58
  • You can use a pipe and bash's `$PIPESTATUS` array. But that won't work with `mapfile`, because it would be executed in a subshell, so the variable assignment wouldn't persist. – Barmar Apr 28 '22 at 15:00
  • Guess the only option is`f="$(mktemp)"; exit 1 > "$f"; mapfile -t tuples < "$f"` then, which does catch the failure – balupton Apr 28 '22 at 15:01

3 Answers3

2

In bash 4.4 or later, process substitutions will set $!, which means you can wait on that process to get its exit status.

#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit

echo 'before'

mapfile -t tuples < <(exit 1)
wait $!

echo 'after'

mapfile itself (in general) won't have a non-zero status, because it's perfectly happy read what, if anything, the process substitution produces.

chepner
  • 497,756
  • 71
  • 530
  • 681
2

You can assign a variable with the output of the command. The variable assignment propagates errors from the command substitution.

t=$(exit 1)
echo 'after'
mapfile -t tuples <<<"$t"
Barmar
  • 741,623
  • 53
  • 500
  • 612
2

If you have Bash 4.2 or later, since you are already setting errexit and pipefail, you can avoid the problem by using:

...
shopt -s lastpipe
exit 1 | mapfile -t tuples

shopt -s lastpipe causes the last command in a pipeline to be run in the current shell. See how does shopt -s lastpipe affect bash script behavior?. In this case it means that a tuples value read by mapfile can be accessed later in the code.

pjh
  • 6,388
  • 2
  • 16
  • 17