[libvirt] [PATCH] schematestutils.sh: improve shell portability: avoid "echo -e"

Jim Meyering jim at meyering.net
Wed Apr 14 12:20:39 UTC 2010


Daniel Veillard wrote:

> On Wed, Apr 14, 2010 at 01:28:54PM +0200, Jim Meyering wrote:
>> echo -e is not portable.
>> This is the sole remaining use in all of libvirt.
>>
>> >From a58cf340b5ebbca2157f43c6f23d4d6f56b848c7 Mon Sep 17 00:00:00 2001
>> From: Jim Meyering <meyering at redhat.com>
>> Date: Wed, 14 Apr 2010 13:25:46 +0200
>> Subject: [PATCH] schematestutils.sh: improve shell portability: avoid "echo -e"
>>
>> * tests/schematestutils.sh: Use printf rather than echo -e.
>> ---
>>  tests/schematestutils.sh |    2 +-
>>  1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/tests/schematestutils.sh b/tests/schematestutils.sh
>> index f172857..f2b3b50 100644
>> --- a/tests/schematestutils.sh
>> +++ b/tests/schematestutils.sh
>> @@ -22,7 +22,7 @@ do
>>
>>      test_result $n $(basename $(dirname $xml))"/"$(basename $xml) $ret
>>      if test "$verbose" = "1" && test $ret != 0 ; then
>> -        echo -e "$cmd\n$result"
>> +        printf '%s\n' "$cmd" "$result"
>>      fi
>>      if test "$ret" != 0 ; then
>>          f=`expr $f + 1`
>
>   I fail to see how printf(1) is any more portable than echo which
> is usually from the shell itself. Sounds to me that
>      echo "$cmd" ; echo "$result"
> does what we want directly or am I mistaken ?

printf is most definitely more portable than "echo -e".
Note that printf has been a shell built-in in just about every
shell in common use for at least 10 years.
Documentation everywhere has been telling developers to prefer
"printf" over "echo-with-any-options" for nearly 20 years.

Here's an excerpt from "info coreutils echo":

       POSIX does not require support for any options, and says that the
    behavior of `echo' is implementation-defined if any STRING contains a
    backslash or if the first argument is `-n'.  Portable programs can use
    the `printf' command if they need to omit trailing newlines or output
    control characters or backslashes.  *Note printf invocation::.

While my objection was to "echo -e", plain 'echo "$var"'
(where $var may be arbitrary) is not terribly reliable.
There are plenty of caveats when using plain "echo".

I.e., the first argument must not start with a "-".
Using printf the way I do above, even if $cmd or $value starts with "-"
or contains backslashes, it will be faithfully reproduced.
However, if you use your pair of "echo" commands
and some $result starts with "-", you'll get misleading output.


Here's the section on "echo" in autoconf's "Limitations of Builtins" section:

`echo'
     The simple `echo' is probably the most surprising source of
     portability troubles.  It is not possible to use `echo' portably
     unless both options and escape sequences are omitted.  Don't
     expect any option.

     Do not use backslashes in the arguments, as there is no consensus
     on their handling.  For `echo '\n' | wc -l', the `sh' of Solaris
     outputs 2, but Bash and Zsh (in `sh' emulation mode) output 1.
     The problem is truly `echo': all the shells understand `'\n'' as
     the string composed of a backslash and an `n'.  Within a command
     substitution, `echo 'string\c'' will mess up the internal state of
     ksh88 on AIX 6.1 so that it will print the first character `s'
     only, followed by a newline, and then entirely drop the output of
     the next echo in a command substitution.

     Because of these problems, do not pass a string containing
     arbitrary characters to `echo'.  For example, `echo "$foo"' is safe
     only if you know that FOO's value cannot contain backslashes and
     cannot start with `-'.

     If this may not be true, `printf' is in general safer and easier
     to use than `echo' and `echo -n'.  Thus, scripts where portability
     is not a major concern should use `printf '%s\n'' whenever `echo'
     could fail, and similarly use `printf %s' instead of `echo -n'.
     For portable shell scripts, instead, it is suggested to use a
     here-document like this:

          cat <<EOF
          $foo
          EOF

     Alternatively, M4sh provides `AS_ECHO' and `AS_ECHO_N' macros
     which choose between various portable implementations: `echo' or
     `print' where they work, `printf' if it is available, or else
     other creative tricks in order to work around the above problems.




More information about the libvir-list mailing list