I have a piece of bash code that works fine.
for thing in "$things"; do
output=$(my_program "$thing" 2>&1)
if [[ $? -eq 0 ]]; then
echo "$(echo "$output" | tail -1)"
else
echo "$(echo "$output" | tail -1)" 1>&2
fi
done
The idea is to echo the last line of the output from the subcommand to stdout if it worked and to stderr if it didn't work.
Is there a better way to achieve this?
-
\$\begingroup\$ Welcome to Code Review! I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have. \$\endgroup\$Toby Speight– Toby Speight2021年03月20日 13:21:33 +00:00Commented Mar 20, 2021 at 13:21
1 Answer 1
Don't quote $things
:
for i in "1 2 3 4"; do
echo "i=$i"
done
i=1 2 3 4
# Better is
for i in 1 2 3 4; do
echo "i=$i"
done
i=1
i=2
i=3
i=4
When my_program
is written well, errors are written to stderr and normal output to stdout. Your bash code is working well, so the program already uses the right returncode.
Can you change the my_program
? When you can add an option -s
(silent/summary), the program can make sure only 1 line is written to stdout (when OK) or stderr (when NOK):
for thing in ${things}; do
my_program -s "${thing}"
done
Or without for-loop
printf "%s\n" ${things} | xargs -L1 -I{} my_program -s "{}"
When you can't change my_program
, or you don't want to, move the special handling to a function and remove the additional echo
commands:
tail_my_program() {
output=$(my_program $* 2>&1)
if (( $? == 0 )); then
tail -1 <<< "${output}"
else
tail -1 <<< "${output}" >&2
fi
}
for thing in ${things}; do
tail_my_program "${thing}"
done
Your original solution and my function have a small bug: When my_program
writes something to stdout after writing an error message to stderr, the stdout message is selected after an exit 1
. This might never happen.