Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have heard that printf is better than echo and I can recall only one instance from my experience where I had to use printf because echo didn't work for feeding some text into some program on RHEL 5.8 and printf did. But apparently, there are other differences and I would like to inquire what they are as well as if there are specific cases when to use one vs the other.

share|improve this question
11  
And we're on Hacker News: news.ycombinator.com/item?id=5625874 –  Renan Apr 29 '13 at 17:07

5 Answers 5

up vote 254 down vote accepted

Basically, it's a portability (and reliability) issue.

Initially, echo didn't accept any option and didn't expand anything. All it was doing was outputting its arguments separated by a space character and terminated by a newline character.

Now, someone thought it would be nice if we could do things like echo "\n\t" to output newline or tab characters, or have an option not to output the trailing newline character.

They then thought harder but instead of adding that functionality to the shell (like perl where inside double quotes, \t actually means a tab character), they added it to echo.

David Korn realized the mistake and introduced a new form of shell quotes: $'...' which was later copied by bash and zsh but it was far too late by that time.

Now when a standard Unix echo receives an argument which contains the two characters \ and t, instead of outputting them, it outputs a tab character. And as soon as it sees \c in an argument, it stops outputting (so the trailing newline is not output either).

Other shells/Unix vendors chose to do it differently: they added a -e option expand escape sequences, and a -n option to not output the trailing newline. Some have a -E to disable escape sequences, some have -n but not -e, the list of escape sequences supported by one echo implementation is not necessarily the same as supported by another.

Sven Mascheck has a nice page that shows the extent of the problem.

On those echo that support options, there's generally no support of a -- to mark the end of options (zsh and possibly others support - for that though), so for instance, it's difficult to output "-n" in many shells.

On some shells like bash1 or ksh932, the behavior even depends on how the shell was compiled or the environment. So two bash echos, even from the same version of bash are not guaranteed to behave the same.

POSIX says: if the first argument is -n or any argument contains backslashes, then the behavior is unspecified. bash echo in that regard is not POSIX in that for instance echo -e is not outputting -e<newline> as POSIX requires. The Unix specification is stricter, it prohibits -n and requires expansion of some escape sequences including the \c one to stop outputting.

Those specifications don't really come to the rescue here given that many implementations are not compliant.

All in all, you don't know what echo "$var" will output unless you can make sure that $var doesn't contain backslash characters and doesn't start with -. The POSIX specification actually does tell us to use printf instead in that case.

So what that means is that you can't use echo to display uncontrolled data. In other words, if you're writing a script and it is taking external input (from the user as arguments, or file names from the file system...), you can't use echo to display it.

This is OK:

echo >&2 Invalid file.

This is not:

echo >&2 "Invalid file: $file"

(Though it will work OK with some (non Unix) echo implementations like bash's when the xpg_echo has not been enabled in one way or another like at compilation time or via the environment).

printf, on the other hand is more reliable, at least when it's limited to the basic usage of echo.

 printf '%s\n' "$var"

Will output the content of $var followed by a newline character regardless of what character it may contain.

 printf %s "$var"

Will output it without the trailing newline character.

Now, there also are differences between printf implementations. There's a core of features that is specified by POSIX, but then there's a lot of extensions. For instance, some support a %q to quote the arguments but how it's done is shell specific, some support \uxxxx for unicode characters. The behavior varies for printf '%10s\n' "$var" in multibyte locales, there are at least three different outcomes of printf %b '\123'

But in the end, if you stick to the POSIX feature set of printf and don't try doing anything fancy with it, you're out of trouble.

But remember the first argument is the format, so shouldn't contain variable/uncontrolled data.

A more reliable echo can be implemented using printf, like:

echo() ( # forking for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%s\n' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%b\n' "$*"
)

The fork can be avoided using local IFS on Linux (the LSB specification mandates local for Linux sh), or by writing it like:

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s "$1"
     shift
  fi
  if [ "$#" -gt 0 ]; then
     printf ' %s' "$@"
  fi
  printf '\n'
}

Notes

1. how bash's echo behaviour can be altered.

With bash, at run time, there are two things that control the behaviour or echo: the xpg_echo bash options and whether bash is in posix mode. posix mode can be enabled if bash is called as sh or if POSIXLY_CORRECT is in the environment or with the the posix option:

Default behaviour on most systems:

$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character

xpg_echo expands sequences as Unix requires:

$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A

It still honours -n and -e (and -E):

$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%

With xpg_echo and POSIX mode:

$ BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ BASHOPTS=xpg_echo ARGV0=sh bash -c 'echo -n "\0101"' # The ARGV0=sh is to pass argv[0] here
-n A
$ BASHOPTS=xpg_echo SHELLOPTS=posix ARGV0=sh bash -c 'echo -n "\0101"'
-n A
$ BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A

This time, bash is both POSIX and Unix conformant. Note that in POSIX mode, bash is still not POSIX conformant as it doesn't output -e in:

 $ SHELLOPTS=posix bash -c 'echo -e'

 $

The default values for xpg_echo and posix can be defined at compilation time with the --enable-xpg-echo-default and --enable-strict-posix-default options to the configure script. That's typically what recent versions of OS/X do to build their /bin/sh. No Unix/Linux implementation/distribution in their right mind would typically do that for /bin/bash though.

2. How ksh93's echo behaviour can be altered.

In ksh93, whether echo expands escape sequences or not and recognises options depends on the content of $PATH.

If $PATH contains a component that contains /5bin or /xpg before the /bin or /usr/bin component then it behave the SysV/Unix way (expands sequences, doesn't accept options). If it finds /ucb or /bsd first, then it behaves the BSD way (-e to enable expansion, recognises -n). The default is system dependant, BSD on Debian:

$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
share|improve this answer
11  
Formidably thorough answer. –  kojiro Feb 23 '13 at 0:15
39  
Glad to have people like you in this community, willing to share their knowledge and enlighten others too. –  NlightNFotis Feb 23 '13 at 8:16
8  
wow. did you, like, write unix? –  Eliran Malka Feb 13 at 14:39
4  
Stephane, a good part of the Bash chops I have learned here and there, I owe them to you. Your commitment to sharing what you know is amazing. I guess the least I could do is say ... thank-you, you rock friend. –  stefgosselin Feb 27 at 4:29
3  
A lot of early unix development happened in isolation, and good software engineering principles like 'when you change the interface, change the name' were not applied. –  Henk Langeveld Mar 14 at 7:57

You might want to use printf for its formatting options. echo is useful when it comes to printing the value of a variable or a (simple) line, but that's all there is to it. printf can basically do what the C version of it can do.

Example usage and capabilities:

Echo

echo "*** Backup shell script ***"
echo
echo "Runtime: $(date) @ $(hostname)"
echo

printf

vech="bike"
printf "%s\n" "$vech"

Sources:

share|improve this answer
    
@0xC0000022L I stand corrected thanks. I didn't notice I linked to the wrong site in my rush to answer the question. Thank you for your contribution and the correction. –  NlightNFotis Feb 22 '13 at 20:18
    
Using echo to print a variable can fail if the value of the variable contains meta-characters. –  Keith Thompson Apr 29 '13 at 15:22

One "advantage", if you want to call it that, would be that you don't have to tell it like echo to interpret certain escape sequences such as \n. It knows to interpret them and won't require an -e to do so.

printf "some\nmulti-lined\ntext\n"

(NB: the last \n is necessary, echo implies it, unless you give the -n option)

versus

echo -e "some\nmulti-lined\ntext"

Note the last \n in printf. At the end of the day it's a matter of taste and requirements what you use: echo or printf.

share|improve this answer
1  
True for /usr/bin/echo and the bash builtin. The dash, ksh and zsh builtin echo does not need -e switch to expand backslash-escaped characters. –  manatwork Feb 23 '13 at 10:35
    
Why the scare quotes? Your wording implies that it isn't necessarily a real advantage. –  Keith Thompson Apr 29 '13 at 15:26
    
@KeithThompson: actually all they are meant to imply is that not everyone may consider it an advantage. –  0xC0000022L Apr 29 '13 at 15:33
    
Could you expand on that? Why wouldn't it be an advantage? The phrase "if you want to call it that" implies pretty strongly that you think it isn't. –  Keith Thompson Apr 29 '13 at 15:36
    
@KeithThompson: I can think of a few scenarios where you'd like to preserve the escape characters to pass them on (e.g. store them in a file). In this case echo is more flexible in a sense, since you can suppress the processing of escape characters. To me, yes indeed, it's a slim advantage. And I would hardly call it that but the OP decided to call it that, so I went with the OP's wording. Hope this helps. –  0xC0000022L Apr 29 '13 at 15:49

One downside of printf is performance because the built-in shell echo is much faster. This comes into play particularly in Cygwin where each instance of a new command causes heavy Windows overhead. When I changed my echo-heavy program from using /bin/echo to the shell's echo the performance almost doubled. It's a trade off between portability and performance. It's not a slam dunk to always use printf.

share|improve this answer
    
printf is built in most shells nowadays (bash, dash, ksh, zsh, yash, some pdksh derivatives... so includes the shells typically found on cygwin as well). The only notable exceptions are some pdksh derivatives. –  Stéphane Chazelas Nov 7 at 13:52

Also printf is faster in primitive types with respect to the echo. this is because the the type of the data is specified in the code. the difference in the speed is not recognised as countable factor unless you are coding a driver.

share|improve this answer
3  
What do you mean by “primitive types”? –  manatwork Feb 23 '13 at 14:57
2  
I don't believe that applies to the printf command. It probably doesn't apply to the printf function, which has to spend time parsing the format string, –  Keith Thompson Apr 29 '13 at 15:25

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.