Hi Ben, Thanks for this analysis.
> Default handling of SIGPIPE > --------------------------- > > b...@blp:~/tmp(0)$ strace -o foo -f bash > b...@blp:~/tmp(0)$ trap - SIGPIPE > b...@blp:~/tmp(0)$ grep ^Sig /proc/$$/status > SigQ: 1/16382 > SigPnd: 0000000000000000 > SigBlk: 0000000000010000 > SigIgn: 0000000000384000 > SigCgt: 000000004b813eff > > SigIgn: ...4000 means SIGPIPE is not SIG_IGN. > SigCgt: ...3eff means SIGPIPE has a registered handler. > > b...@blp:~/tmp(0)$ echo hi | ./a.out > bash: echo: write error: Broken pipe An even easier way to reproduce it is to run $ echo hi | true 10 times at a bash prompt. In bash 3.2, it frequently prints the error message. > Your gnulib-tool comment refers to Chet Ramey's email at > http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html. > I think that he is saying the same thing, actually: > > The second is that the echo builtin in bash-3.2 displays a message on > a write error, instead of letting the exit status communicate the error. > When the shell receives SIGPIPE and handles it without exiting, writes > to that pipe return -1/EPIPE, and the echo builtin reports the error. In > earlier versions, you wouldn't have seen the message. > > The bash 3.2 "printf" builtin doesn't have this problem though. Aha! So it's really a bug in the 'echo' built-in, and using 'printf' is a work-around. I'm updating gnulib-tool: The function func_reset_sigpipe had no effect, since the bash bug occurs also in the _default_ situation (no inherited trap or ignored signal). Also the comment "When "trap '' SIGPIPE" is in effect, the behaviour (at least with bash) is to terminate the current process with an error message." was wrong, because it applies only to the 'echo' built-in, not to other built-ins. 2010-02-21 Bruno Haible <br...@clisp.org> Really work around around "broken pipe" error message from bash 3.2. * gnulib-tool (func_reset_sigpipe): Remove function. (echo): In bash 3.2, define to a function that uses printf. Analyzed by Ralf Wildenhues, Chet Ramey, Ben Pfaff. --- gnulib-tool.orig Sun Feb 21 13:24:10 2010 +++ gnulib-tool Sun Feb 21 13:20:19 2010 @@ -661,39 +661,20 @@ fi } -# func_reset_sigpipe -# Resets SIGPIPE to its default behaviour. SIGPIPE is signalled when a process -# writes into a pipe with no readers, i.e. a pipe where all readers have -# already closed their file descriptor that read from it or exited entirely. -# The default behaviour is to terminate the current process without an error -# message. -# When "trap '' SIGPIPE" is in effect, the behaviour (at least with bash) is to -# terminate the current process with an error message. -# This function should be called at the beginning of a command that only -# produces output to stdout (i.e. no side effects!), when the command that -# will read from this pipe might prematurely exit or close its standard input -# descriptor. -if test -n "$BASH_VERSION"; then - # The problem has only been reported with bash. Probably it occurs only with - # bash-3.2. For the reasons, see - # <http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html>. - # Note that Solaris sh does not understand "trap - SIGPIPE". - func_reset_sigpipe () - { - trap - SIGPIPE - } -else - func_reset_sigpipe () - { - : - } -fi - -# Ensure an 'echo' command that does not interpret backslashes. -# Test cases: +# Ensure an 'echo' command that +# 1. does not interpret backslashes and +# 2. does not print an error message "broken pipe" when writing into a pipe +# with no writers. +# +# Test cases for problem 1: # echo '\n' | wc -l prints 1 when OK, 2 when KO # echo '\t' | grep t > /dev/null has return code 0 when OK, 1 when KO -# This problem is a weird heritage from SVR4. BSD got it right (except that +# Test cases for problem 2: +# echo hi | true frequently prints +# "bash: echo: write error: Broken pipe" +# to standard error in bash 3.2. +# +# Problem 1 is a weird heritage from SVR4. BSD got it right (except that # BSD echo interprets '-n' as an option, which is also not desirable). # Nowadays the problem occurs in 4 situations: # - in bash, when the shell option xpg_echo is set (bash >= 2.04) @@ -716,6 +697,12 @@ # - otherwise: respawn using /bin/sh and rely on the workarounds. # When respawning, we pass --no-reexec as first argument, so as to avoid # turning this script into a fork bomb in unlucky situations. +# +# Problem 2 is specific to bash 3.2 and affects the 'echo' built-in, but not +# the 'printf' built-in. See +# <http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html> +# <http://lists.gnu.org/archive/html/bug-gnulib/2010-02/msg00154.html> +# The workaround is: define echo to a function that uses the printf built-in. have_echo= if echo '\t' | grep t > /dev/null; then have_echo=yes # Lucky! @@ -823,6 +810,15 @@ exec /bin/sh "$0" --no-reexec "$@" exit 127 fi +# Now handle problem 2, specific to bash 3.2. +case "$BASH_VERSION" in + 3.2*) + echo () + { + printf '%s\n' "$*" + } + ;; +esac if test -z "$have_echo"; then func_fatal_error "Shell does not support 'echo' correctly. Please install GNU bash and set the environment variable CONFIG_SHELL to point to it." fi @@ -2029,8 +2025,7 @@ func_filter_filelist lib_files "$nl" "$all_files" 'lib/' '' 'lib/' '' # Remove $already_mentioned_files from $lib_files. echo "$lib_files" | LC_ALL=C sort -u > "$tmp"/lib-files - extra_files=`func_reset_sigpipe; \ - for f in $already_mentioned_files; do echo $f; done \ + extra_files=`for f in $already_mentioned_files; do echo $f; done \ | LC_ALL=C sort -u | LC_ALL=C join -v 2 - "$tmp"/lib-files` if test -n "$extra_files"; then echo "EXTRA_DIST +=" $extra_files @@ -2266,7 +2261,7 @@ handledmodules=`for m in $handledmodules $inmodules_this_round; do echo $m; done | LC_ALL=C sort -u` # Remove $handledmodules from $inmodules. for m in $inmodules; do echo $m; done | LC_ALL=C sort -u > "$tmp"/queued-modules - inmodules=`func_reset_sigpipe; echo "$handledmodules" | LC_ALL=C join -v 2 - "$tmp"/queued-modules` + inmodules=`echo "$handledmodules" | LC_ALL=C join -v 2 - "$tmp"/queued-modules` done modules=`for m in $outmodules; do echo $m; done | LC_ALL=C sort -u` rm -f "$tmp"/queued-modules @@ -3354,8 +3349,7 @@ fi # Determine tests-related module list. echo "$final_modules" | LC_ALL=C sort -u > "$tmp"/final-modules - testsrelated_modules=`func_reset_sigpipe - for module in $main_modules; do + testsrelated_modules=`for module in $main_modules; do if test \`func_get_applicability $module\` = main; then echo $module fi @@ -4256,8 +4250,7 @@ if test -f "$destdir/$dir$ignore"; then if test -n "$dir_added" || test -n "$dir_removed"; then sed -e "s|^$anchor||" < "$destdir/$dir$ignore" | LC_ALL=C sort > "$tmp"/ignore - (func_reset_sigpipe - echo "$dir_added" | sed -e '/^$/d' | LC_ALL=C sort -u \ + (echo "$dir_added" | sed -e '/^$/d' | LC_ALL=C sort -u \ | LC_ALL=C join -v 1 - "$tmp"/ignore > "$tmp"/ignore-added echo "$dir_removed" | sed -e '/^$/d' | LC_ALL=C sort -u \ | LC_ALL=C join -v 1 - "$tmp"/ignore > "$tmp"/ignore-removed