Bourne | Ash |  #!  | find | ARG_MAX | Shells | whatshell | portability | permissions | UUOC | ancient | - | ../Various | HOME
"$@" | echo/printf | set -e | test | tty defs | tty chars | $() vs ) | IFS | using siginfo | nanosleep | line charset | locale


echo(1) and printf(1)

Behaviour of the "echo" command or the shell built-in, respectively.
Availability of "printf", and implementation variations (especially handling of escape sequences).

2011-02-27 (see recent changes)


1.) Variations in echo implementations

History

About the history, from Gunnar Ritter in <3B98D626.HL11B46E@bigfoot.de> (rough translation by me), motivating this page:

"Research Unix -> 32v -> BSD had "-n",
PWB/Unix -> System III -> System V [had] the escape sequences
and then it became a jumble."

6th edition echo research unix didn't know any features.
7th edition echo implemented -n
PWB/Unix1.0 echo (derived from 6th edition) implemented \n, \c, \\, and \0xx
System III (and SVR1) echo knew \b, \c, \f, \n, \r, \t, \\, and \0xx

Nowadays, echo(1) is only portable if you omit flags and escape sequences.
Use printf(1) instead, if you need more than plain text.
printf was introduced with the Ninth Edition System (reference in SUSv3).
It was added to more widely distributed Unix flavours with 4.3BSD-Reno and with SVR4.
Meanwhile printf is required by POSIX (SUSv1).

Portability

Traditional portability of echo is one issue. But what does POSIX say about options and backslash sequences?

POSIX doesn't support plain options:
SUSv2 states that "Implementations will not support any options."
Since SUSv3 this reads "Implementations shall not support any options."

But the BSD-like behaviour of suppressing newline with "-n" is not always forbidden:
The behaviour with an operand containing "-n" and also the behaviour about backslash sequences is implementation defined.
This gets the various traditional implementations into the boat.

But if the XSI option (X/Open System Interfaces) is supported on the system, then echo is well defined and intended to behave
as specified by the System V Interface Definition, version 3: "-n" is not recognized and backslash sequences are defined.

More background about this distinction is explained by Don Cragun on the austin group mailing list:

"The specification of \0xxx in echo with the XSI option is intended to behave as UNIX System V behaved as specified by the System V Interface Definition, version 3 (SVID3).
(SVID3 was one of the base documents of the original POSIX.2 standard, but since BSD systems and UNIX System V systems had different behavior for echo,
the original POSIX.2 standard left the behavior unspecified (allowing the then current implementations to continue to behave as they had).
The X/Open Portability Guide, Issue 3 continued to require the behavior as specified in SVID3.
When the original POSIX.2 and XPG3 were merged in a later revision of the standards, we got the XSI option (which required the System V behavior);
but when XSI option support is not claimed the old BSD or System V behavior is allowed."

If you still have to supress newline with echo for some reason (with the same script on different systems),
a possible workaround is the following code ( ${1+"$@"} instead of "$@" addresses ancient pre-SVR3 shells):

    if [ "X`echo -n`" = "X-n" ]; then
      echo_n() { echo ${1+"$@"}"\c"; }
    else
      echo_n() { echo -n ${1+"$@"}; }
    fi 

A closer look at various implementations

(\a is a representative for the other escape sequences)

Footnotes for the first table

"-e" feature enabled when using this flag
"?" implementation defined
[hp-ux] On HP-UX 10, the POSIX shell (/bin/sh) behaves like the Korn shell
[s4bsd] SunOS 4 with /usr/bin preceding /usr/5bin in PATH.
The echo builtin mimics the according external command.
[s5bsd] SunOS 5 enables BSD compatibility, if /usr/ucb precedes /usr/bin in PATH.
On x86, this is also enabled, if SYSV3 is set in the environment
[s4sv] SunOS 4 with /usr/5bin preceding /usr/bin in PATH.
The SysV version knows the escape sequences "\b \c \f \n \r \t \v \xxx", but not "\a" (ASCII-BEL).
[minix3] printf(1) has not been documented yet at the time of this writing (06/2010, v3.1.6).
It was added in jan '87 and released with v2.0.3. It's located in /usr/bin/.
[busybox]: busybox added printf(1) at about v0.27, may 1995.


2.) printf: variations in the handling of escape seqences

The following table lists tests results.
Keep in mind that some examples use unportable input to illustrate variations.

commandline bash-2.05b bash-4.0 ash-0.4.0 dash-
0.5.5.1
ksh93-k ksh93-t GNU core-
utils 5.97
OpenServer
5.0.6
OSF/1
V4.0B
EP/IX 2.2.1A SunOS 5.9
printf '\a'           | od -b -A n|sed 2d   007   007   007   007   007   007   007   007   007   007   007
printf '\b'           | od -b -A n|sed 2d   010   010   010   010   010   010   010   010   010   010   010
printf '\t'            | od -b -A n|sed 2d   011   011   011   011   011   011   011   011   011   011   011
printf '\n'           | od -b -A n|sed 2d   012   012   012   012   012   012   012   012   012   012   012
printf '\v'           | od -b -A n|sed 2d   013   013   013   013   013   013   013   013   013   013   013
printf '\f'           | od -b -A n|sed 2d   014   014   014   014   014   014   014   014   014   014   014
printf '\r'           | od -b -A n|sed 2d   015   015   015   015   015   015   015   015   015   015   015
printf '.\c.'         | od -b -A n|sed 2d   056 134 143 056   056 134 143 056   056   056 134 143 056   056 143 056   056 156   056 134 143 056   134 143   056 134 143   056 134 143 056   056 134 143 056
printf '%b' '.\c.' | od -b -A n|sed 2d   056   056   056   056   056   056   056   056   056   056   056
printf '\d'           | od -b -A n|sed 2d   134 144   134 144   134 144   134 144   144   144   134 144   134 144   134   134 144   134 144
printf '\g'           | od -b -A n|sed 2d   134 147   134 147   134 147   134 147   147   147   134 147   134 147   134   134 147   134 147
printf '\h'           | od -b -A n|sed 2d   134 150   134 150   134 150   134 150   150   150   134 150   134 150   134   134 150   134 150
printf '\i'            | od -b -A n|sed 2d   134 151   134 151   134 151   134 151   151   151   134 151   134 151   134   134 151   134 151
printf '\j'            | od -b -A n|sed 2d   134 152   134 152   134 152   134 152   152   152   134 152   134 152   134   134 152   134 152
printf '\k'           | od -b -A n|sed 2d   134 153   134 153   134 153   134 153   153   153   134 153   134 153   134   134 153   134 153
printf '\l'            | od -b -A n|sed 2d   134 154   134 154   134 154   134 154   154   154   134 154   134 154   134   134 154   134 154
printf '\m'          | od -b -A n|sed 2d   134 155   134 155   134 155   134 155   155   155   134 155   134 155   134   134 155   134 155
printf '\o'           | od -b -A n|sed 2d   134 157   134 157   134 157   134 157   157   157   134 157   134 157   134   134 157   134 157
printf '\p'           | od -b -A n|sed 2d   134 160   134 160   134 160   134 160   160   160   134 160   134 160   134   134 160   134 160
printf '\q'           | od -b -A n|sed 2d   134 161   134 161   134 161   134 161   161   161   134 161   134 161   134   134 161   134 161
printf '\s'           | od -b -A n|sed 2d   134 163   134 163   134 163   134 163   040   163   134 163   134 163   134   134 163   134 163
printf '\u'           | od -b -A n|sed 2d   134 165   134 165   N/A   134 165   165   000   134 165   134 165   134   134 165   134 165
printf '\w'           | od -b -A n|sed 2d   134 167   134 167   134 167   134 167   167   167   134 167   134 167   134   134 167   134 167
printf '\y'           | od -b -A n|sed 2d   134 171   134 171   134 171   134 171   171   171   134 171   134 171   134   134 171   134 171
printf '\z'           | od -b -A n|sed 2d   134 172   134 172   134 172   134 172   172   172   134 172   134 172   134   134 172   134 172
printf '\e'           | od -b -A n|sed 2d   033   033   134 145   134 145   145   033   033   033   033   033   033
printf '\033'       | od -b -A n|sed 2d   033   033   033   033   033   033   033   033   033   033   033
printf '\33'         | od -b -A n|sed 2d   033   033   033   033   033   033   033   033   033   033   033
printf '%b' '\33'  | od -b -A n|sed 2d   033   033   033   033   134 063 063   134 063 063   033   033   N/A   033   033
printf '%b' '\033'     | od -b -A n|sed 2d   033   033   033   033   033   033   033   033   033   033   033
printf '%b' '\0033'   | od -b -A n|sed 2d   033   033   033   033   033   033   033   033   033   033   033
printf '\0033'           | od -b -A n|sed 2d   033   003 063   003 063   003 063   003 063   003 063   003 063   003 063   003 063   003 063   033
printf '\0033'           | od -b -A n|sed 2d   033   003 063   003 063   003 063   003 063   003 063   003 063   003 063   003 063   003 063   033
commandline bash-2.05b bash-4.0 ash-0.4.0 dash-
0.5.5.1
ksh93-k ksh93-t GNU core-
utils 5.97
OpenServer
5.0.6
OSF/1
V4.0B
EP/IX 2.2.1A SunOS 5.9
printf '%d\n' '"a'   97   97   97   97   97   97   97   97   97   2146938837   97


<http://www.in-ulm.de/~mascheck/various/echo+printf/>

Sven Mascheck