I understand here cat input.txt
is a place holder for some arbitrary command. For cat input.txt
specifically, the solution would be easy: just use input redirection from input.txt
instead.
The exit code will be available in next expansions in the here string. For instance:
cat <<< "$(exit 3)$?"
Outputs 3. So you could do something like:
#! /bin/bash -
set -o nounset -o pipefail -o errexit
unset -v ret
{
[ "$ret" -eq 0 ] || exit "$ret"
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" || exit
done
} <<< "$(cat input.txt)"${ret[1+(ret=$?)]}"
echo success
Which appears to work with bash 5.0 at least.
Or you could use a temporary variable:
#! /bin/bash -
set -o nounset -o pipefail -o errexit
output_minus_trailing_newlines=$(cat input.txt)
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" || exit
done <<< "$output_minus_trailing_newlines"
echo success
In either case, if the command fails, none of its output will be processed.
You could also use a pipe and the lastpipe
option if you don't want the loop to run in a subshell.
#! /bin/bash -
set -o nounset -o pipefail -o errexit
shopt -s lastpipe
cat input.txt |
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" || exit
done
echo success
Then the output will be processed as it comes. It's only after the pipeline exits that the shell will exit if the command failed thanks to the combination of errexit
(-e
) and pipefail
. In that case, trailing empty lines are preserved.
Same using process substitution:
#! /bin/bash -
set -o nounset -o pipefail -o errexit
{
pid="$!"
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" || exit
done
wait "$pid"
} < <(cmd input.txt)
echo success
Or you could use a tempfile (here docs and here strings used to be implemented with temp files in bash).
#! /bin/bash -
set -o nounset -o pipefail -o errexit
tmp=$(mktemp)
{
rm -f -- "$tmp"
cat input.txt >&3
exec 3>&-
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" || exit
done
wait "$pid"
} 3> "$tmp" < "$tmp"
echo success
This way, the output is not processed if the command failed and you don't store the output (several times) in memory and the trailing empty lines are preserved.
In any case, see Why is using a shell loop to process text considered bad practice?
cat input.txt
is just an example. My actual command is long running process and I can't predict if it will fail or not. But I want my wrapping script to fail if the command fails. – Thomas Leplus Dec 16 '22 at 03:00