I'm trying to understand the reason why echo behaves differently than echo "Hi"|cat when used in a bash script, with broken pipe
Behaviour :
echoimmediately terminates the scriptecho "Hi"|catthe pipeline terminates but the script continues
Sample reproducing steps
script_with_echo.sh :
#!/bin/bash
mkfifo ii
cat<ii >/dev/null & echo "KILL : $!"
exec 3>ii
rm ii
while true; do read LINE; echo "$LINE" >&3; echo "$?">/some/external/file;done
script_with_echo_cat.sh :
#!/bin/bash
mkfifo ii
cat<ii >/dev/null & echo "KILL : $!"
exec 3>ii
rm ii
while true; do read LINE; echo "$LINE"|cat>&3; echo "$?">/some/external/file;done
- run a script on terminal-1 (note
KILL : <PID>) - input some sample lines to terminal-1 and verify that
0(success exit code) is being written to/some/external/file - run
kill -9 <PID>from terminal-2 - input another sample line on terminal-1
-
- If
script_with_echo.shwas being executed in step 1, the script immediately terminates (and no error code is written to/some/external/file) - If
script_with_echo_cat.shwas being executed in step 1, the script continues normally (and for every subsequent sample line inputs, error code 141 (SIGPIPE) is being written to/some/external/file, which is expected)
- If
Why does this different behavior arise?
CodePudding user response:
why echo behaves differently than echo "Hi"|cat when used in a bash script
In the case of echo >&3 when the pipe connect to 3rd file descriptor is closed, then the bash process is killed by SIGPIPE. This is mostly because echo is a builtin command - it is executed by bash itself without any fork()ing.
In the case of echo | cat >&3 when the pipe connected to 3rd file descriptor is closed, then the child process cat is killed by SIGPIPE, in which case the parent process continues to live, so Bash can handle the exit status.
Compare strace -ff bash -c 'mkfifo ii; cat <ii >/dev/null & pid=$! ; exec 3>ii ; kill $pid ; rm ii ; echo something >&3' vs ....; /bin/echo something >&3'.
