Eric Blake wrote: > POSIX requires that echo interpret backslash sequences.
POSIX:2001 Technical Corrigendum 1 [1] mentions that BSD echo is POSIX compliant. Eric Blake and Paul Eggert wrote: > POSIX even admits this, and recommends the use of printf instead. > ... > One advantage of using printf is that it won't mishandle a string that > happens to equal "-n". But I don't want to use printf %s\n in 500 places because 1) It is less legible. 2) Using a complex primitive for a simple task requires more introspection for being well optimized (similar to what we see in C: how many compilers optimize printf("%s",s) to puts(s)?). 3) It is a heavy slowdown for FreeBSD /bin/sh, which has 'echo' built-in but not 'printf', 4) The argument size is inherently limited to the maximum argument size that can be passed to programs, and this limit is ridiculously small on many Linux systems: 256 KB, which is less than 0.1% of main memory. 5) I would not be surprised if some 'printf' implementations had 4 KB limits. Built-in echo and an echo emulation that uses 'cat' do not have these problems. > printf is not yet universally portable, but at least it is a shell builtin > on many common shells (but notably not a builtin in Solaris' /bin/sh). Which are these "many common shells"? It is not a builtin in ksh. It is not a builtin in the various vendors' /bin/sh. The only shell that I found that has 'printf' built-in is bash >= 2.0. > Since forking external programs matters most on cygwin and mingw, where > printf is always available as a builtin, it is one of the lightest-weight > alternatives to echo when you are worried about the number of processes. OK, I took this into consideration and optimized the 'echo' emulation in the case of bash versions >= 2.0, < 2.04. Bruno [1] http://www.opengroup.org/austin/docs/austin_155.txt 2007-06-23 Bruno Haible <[EMAIL PROTECTED]> * gnulib-tool (echo): Add workarounds also for bash versions < 2.04. * tests/test-echo.sh: Likewise. *** gnulib-tool 23 Jun 2007 07:40:58 -0000 1.236 --- gnulib-tool 23 Jun 2007 09:45:35 -0000 *************** *** 415,430 **** # Test cases: # 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. # Nowadays the problem occurs in 4 situations: ! # - in bash, when the shell option xpg_echo is set, # - in zsh, when sh-emulation is not set, # - in ksh (e.g. AIX /bin/sh and Solaris /usr/xpg4/bin/sh are ksh instances, # and HP-UX /bin/sh and IRIX /bin/sh behave similarly), # - in Solaris /bin/sh and OSF/1 /bin/sh. # We try the following workarounds: # - for all: respawn using $CONFIG_SHELL if that is set and works. ! # - for bash: unset the shell option xpg_echo. # - for zsh: turn sh-emulation on. # - for ksh: alias echo to a function that uses cat of a here document. # - for Solaris /bin/sh: respawn using /bin/ksh and rely on the ksh workaround. --- 415,435 ---- # Test cases: # 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 ! # 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) ! # or when it was built with --enable-usg-echo-default (bash >= 2.0) ! # or when it was built with DEFAULT_ECHO_TO_USG (bash < 2.0), # - in zsh, when sh-emulation is not set, # - in ksh (e.g. AIX /bin/sh and Solaris /usr/xpg4/bin/sh are ksh instances, # and HP-UX /bin/sh and IRIX /bin/sh behave similarly), # - in Solaris /bin/sh and OSF/1 /bin/sh. # We try the following workarounds: # - for all: respawn using $CONFIG_SHELL if that is set and works. ! # - for bash >= 2.04: unset the shell option xpg_echo. ! # - for bash >= 2.0: define echo to a function that uses the printf built-in. ! # - for bash < 2.0: define echo to a function that uses cat of a here document. # - for zsh: turn sh-emulation on. # - for ksh: alias echo to a function that uses cat of a here document. # - for Solaris /bin/sh: respawn using /bin/ksh and rely on the ksh workaround. *************** *** 445,457 **** exec $CONFIG_SHELL "$0" --no-reexec "$@" exit 127 fi ! # For bash: unset the shell option xpg_echo. if test -z "$have_echo" \ && test -n "$BASH_VERSION" \ && (shopt -o xpg_echo; echo '\t' | grep t > /dev/null) 2>/dev/null; then shopt -o xpg_echo have_echo=yes fi # For zsh: turn sh-emulation on. if test -z "$have_echo" \ && test -n "$ZSH_VERSION" \ --- 450,486 ---- exec $CONFIG_SHELL "$0" --no-reexec "$@" exit 127 fi ! # For bash >= 2.04: unset the shell option xpg_echo. if test -z "$have_echo" \ && test -n "$BASH_VERSION" \ && (shopt -o xpg_echo; echo '\t' | grep t > /dev/null) 2>/dev/null; then shopt -o xpg_echo have_echo=yes fi + # For bash >= 2.0: define echo to a function that uses the printf built-in. + # For bash < 2.0: define echo to a function that uses cat of a here document. + # (There is no win in using 'printf' over 'cat' if it is not a shell built-in.) + if test -z "$have_echo" \ + && test -n "$BASH_VERSION"; then \ + if type printf 2>/dev/null | grep / > /dev/null; then + # 'printf' is not a shell built-in. + echo () + { + cat <<EOF + $* + EOF + } + else + # 'printf' is a shell built-in. + echo () + { + printf '%s\n' "$*" + } + fi + if echo '\t' | grep t > /dev/null; then + have_echo=yes + fi + fi # For zsh: turn sh-emulation on. if test -z "$have_echo" \ && test -n "$ZSH_VERSION" \ *************** *** 464,470 **** # Therefore, for an alias to take effect, the alias definition command has # to be executed before the command which references the alias is read." # Because of this, we have to play strange tricks with have_echo, to ensure ! # that the top-level statement containing the test start after the 'alias' # command. if test -z "$have_echo"; then bsd_echo () --- 493,499 ---- # Therefore, for an alias to take effect, the alias definition command has # to be executed before the command which references the alias is read." # Because of this, we have to play strange tricks with have_echo, to ensure ! # that the top-level statement containing the test starts after the 'alias' # command. if test -z "$have_echo"; then bsd_echo () *** tests/test-echo.sh 23 Jun 2007 02:02:04 -0000 1.1 --- tests/test-echo.sh 23 Jun 2007 09:45:35 -0000 *************** *** 20,35 **** # Test cases: # 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. # Nowadays the problem occurs in 4 situations: ! # - in bash, when the shell option xpg_echo is set, # - in zsh, when sh-emulation is not set, # - in ksh (e.g. AIX /bin/sh and Solaris /usr/xpg4/bin/sh are ksh instances, # and HP-UX /bin/sh and IRIX /bin/sh behave similarly), # - in Solaris /bin/sh and OSF/1 /bin/sh. # We try the following workarounds: # - for all: respawn using $CONFIG_SHELL if that is set and works. ! # - for bash: unset the shell option xpg_echo. # - for zsh: turn sh-emulation on. # - for ksh: alias echo to a function that uses cat of a here document. # - for Solaris /bin/sh: respawn using /bin/ksh and rely on the ksh workaround. --- 20,40 ---- # Test cases: # 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 ! # 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) ! # or when it was built with --enable-usg-echo-default (bash >= 2.0) ! # or when it was built with DEFAULT_ECHO_TO_USG (bash < 2.0), # - in zsh, when sh-emulation is not set, # - in ksh (e.g. AIX /bin/sh and Solaris /usr/xpg4/bin/sh are ksh instances, # and HP-UX /bin/sh and IRIX /bin/sh behave similarly), # - in Solaris /bin/sh and OSF/1 /bin/sh. # We try the following workarounds: # - for all: respawn using $CONFIG_SHELL if that is set and works. ! # - for bash >= 2.04: unset the shell option xpg_echo. ! # - for bash >= 2.0: define echo to a function that uses the printf built-in. ! # - for bash < 2.0: define echo to a function that uses cat of a here document. # - for zsh: turn sh-emulation on. # - for ksh: alias echo to a function that uses cat of a here document. # - for Solaris /bin/sh: respawn using /bin/ksh and rely on the ksh workaround. *************** *** 50,62 **** exec $CONFIG_SHELL "$0" --no-reexec "$@" exit 127 fi ! # For bash: unset the shell option xpg_echo. if test -z "$have_echo" \ && test -n "$BASH_VERSION" \ && (shopt -o xpg_echo; echo '\t' | grep t > /dev/null) 2>/dev/null; then shopt -o xpg_echo have_echo=yes fi # For zsh: turn sh-emulation on. if test -z "$have_echo" \ && test -n "$ZSH_VERSION" \ --- 55,91 ---- exec $CONFIG_SHELL "$0" --no-reexec "$@" exit 127 fi ! # For bash >= 2.04: unset the shell option xpg_echo. if test -z "$have_echo" \ && test -n "$BASH_VERSION" \ && (shopt -o xpg_echo; echo '\t' | grep t > /dev/null) 2>/dev/null; then shopt -o xpg_echo have_echo=yes fi + # For bash >= 2.0: define echo to a function that uses the printf built-in. + # For bash < 2.0: define echo to a function that uses cat of a here document. + # (There is no win in using 'printf' over 'cat' if it is not a shell built-in.) + if test -z "$have_echo" \ + && test -n "$BASH_VERSION"; then \ + if type printf 2>/dev/null | grep / > /dev/null; then + # 'printf' is not a shell built-in. + echo () + { + cat <<EOF + $* + EOF + } + else + # 'printf' is a shell built-in. + echo () + { + printf '%s\n' "$*" + } + fi + if echo '\t' | grep t > /dev/null; then + have_echo=yes + fi + fi # For zsh: turn sh-emulation on. if test -z "$have_echo" \ && test -n "$ZSH_VERSION" \ *************** *** 69,75 **** # Therefore, for an alias to take effect, the alias definition command has # to be executed before the command which references the alias is read." # Because of this, we have to play strange tricks with have_echo, to ensure ! # that the top-level statement containing the test start after the 'alias' # command. if test -z "$have_echo"; then bsd_echo () --- 98,104 ---- # Therefore, for an alias to take effect, the alias definition command has # to be executed before the command which references the alias is read." # Because of this, we have to play strange tricks with have_echo, to ensure ! # that the top-level statement containing the test starts after the 'alias' # command. if test -z "$have_echo"; then bsd_echo ()