Why is printf better than echo?
I have heard that printf
is better than echo
. 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 but 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.
text-processing echo printf
add a comment |
I have heard that printf
is better than echo
. 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 but 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.
text-processing echo printf
add a comment |
I have heard that printf
is better than echo
. 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 but 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.
text-processing echo printf
I have heard that printf
is better than echo
. 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 but 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.
text-processing echo printf
text-processing echo printf
edited May 15 '15 at 1:44
Sildoreth
86831535
86831535
asked Feb 22 '13 at 19:49
amphibient
4,564103669
4,564103669
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
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 "nt"
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/versions chose to do it differently: they added a -e
option to 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
implementations 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"
with echo
in many shells.
On some shells like bash
1 or ksh93
2 or yash
($ECHO_STYLE
variable), the behavior even depends on how the shell was compiled or the environment (GNU echo
's behaviour will also change if $POSIXLY_CORRECT
is in the environment, zsh
's with its bsd_echo
option, some pdksh-based with their posix
option or whether they're called as sh
or not). So two bash
echo
s, 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. Even some certified systems like macOS4 are not compliant.
To really represent the current reality, POSIX should actually say: if the first argument matches the ^-([eEn]*|-help|-version)$
extended regexp or any argument contains backslashes (or characters whose encoding contains the encoding of the backslash character like α
in locales using the BIG5 charset), then the behaviour is unspecified.
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 compliant) echo
implementations like bash
's when the xpg_echo
option has not been enabled in one way or another like at compilation time or via the environment).
file=$(echo "$var" | tr ' ' _)
is not OK in most implementations (exceptions being yash
with ECHO_STYLE=raw
(with the caveat that yash
's variables can't hold arbitrary sequences of bytes so not arbitrary file names) and zsh
's echo -E - "$var"
).
printf
, on the other hand is more reliable, at least when it's limited to the basic usage of echo
.
printf '%sn' "$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 are a lot of extensions. For instance, some support a %q
to quote the arguments but how it's done varies from shell to shell, some support uxxxx
for unicode characters. The behavior varies for printf '%10sn' "$var"
in multi-byte locales, there are at least three different outcomes for printf %b '123'
But in the end, if you stick to the POSIX feature set of printf
and don't try doing anything too 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() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
The subshell (which implies spawning an extra process in most shell implementations) can be avoided using local IFS
with many shells, 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 of echo
(beside enable -n echo
or redefining echo
as a function or alias):
the xpg_echo
bash
option 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 "101"'
101% # the % here denotes the absence of newline character
xpg_echo
expands sequences as UNIX requires:
$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A
It still honours -n
and -e
(and -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%
With xpg_echo
and POSIX mode:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-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:
$ env 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 BSD3 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
3. BSD for echo -e?
The reference to BSD for the handling of the -e
option is a bit misleading here. Most of those different and incompatible echo
behaviours were all introduced at Bell labs:
n
,ooo
,c
in Programmer's Work Bench UNIX (based on Unix V6), and the rest (b
,r
...) in Unix System IIIRef.
-n
in Unix V7 (by Dennis RitchieRef)
-e
in Unix V8 (by Dennis RitchieRef)
-E
itself possibly initially came frombash
(CWRU/CWRU.chlog in version 1.13.5 mentions Brian Fox adding it on 1992-10-18, GNUecho
copying it shortly after in sh-utils-1.8 released 10 days later)
While the echo
builtin of the sh
or BSDs have supported -e
since the day they started using the Almquist shell for it in the early 90s, the standalone echo
utility to this day doesn't support it there (FreeBSD echo
still doesn't support -e
, though it does support -n
like Unix V7 (and also c
but only at the end of the last argument)).
The handling of -e
was added to ksh93
's echo
when in the BSD universe in the ksh93r version released in 2006 and can be disabled at compilation time.
4. macOS echo
Most versions of macOS have received UNIX certification from the OpenGroup.
Their sh
builtin echo
is compliant as it's bash
(a very old version) built with xpg_echo
enabled by default, but their stand-alone echo
utility is not. env echo -n
outputs nothing instead of -n<newline>
, env echo 'n'
outputs n<newline>
instead of <newline><newline>
.
That /bin/echo
is the one from FreeBSD which suppresses newline output if the first argument is -n
or (since 1995) if the last argument ends in c
, but doesn't support any other backslash sequences required by UNIX, not even \
. Which means it's not only difficult but impossible to have it output -n<newline>
or anythingc<newline>
in one invocation.
13
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 '14 at 7:57
6
As a note, the one (and maybe only) advantage of havingecho
expand thex
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (likeexecve()
) can't take arbitrary sequences of bytes)
– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, evenprintf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is inbosh
and the page from Sven Maschek does not list the problems with null bytes via.
– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
add a comment |
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 "%sn" "$vech"
Sources:
- http://bash.cyberciti.biz/guide/Echo_Command
- man echo
- man printf
@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
7
Usingecho
to print a variable can fail if the value of the variable contains meta-characters.
– Keith Thompson
Apr 29 '13 at 15:22
add a comment |
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 "somenmulti-linedntextn"
(NB: the last n
is necessary, echo
implies it, unless you give the -n
option)
versus
echo -e "somenmulti-linedntext"
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
.
1
True for/usr/bin/echo
and thebash
builtin. Thedash
,ksh
andzsh
builtinecho
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
2
@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
2
Or you could just doprintf '%sn' 'foobar
.
– nyuszika7h
Mar 9 '14 at 17:36
|
show 3 more comments
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
.
10
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 somepdksh
derivatives.
– Stéphane Chazelas
Nov 7 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to useprintf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.
– schily
Jul 3 at 14:46
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f65803%2fwhy-is-printf-better-than-echo%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
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 "nt"
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/versions chose to do it differently: they added a -e
option to 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
implementations 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"
with echo
in many shells.
On some shells like bash
1 or ksh93
2 or yash
($ECHO_STYLE
variable), the behavior even depends on how the shell was compiled or the environment (GNU echo
's behaviour will also change if $POSIXLY_CORRECT
is in the environment, zsh
's with its bsd_echo
option, some pdksh-based with their posix
option or whether they're called as sh
or not). So two bash
echo
s, 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. Even some certified systems like macOS4 are not compliant.
To really represent the current reality, POSIX should actually say: if the first argument matches the ^-([eEn]*|-help|-version)$
extended regexp or any argument contains backslashes (or characters whose encoding contains the encoding of the backslash character like α
in locales using the BIG5 charset), then the behaviour is unspecified.
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 compliant) echo
implementations like bash
's when the xpg_echo
option has not been enabled in one way or another like at compilation time or via the environment).
file=$(echo "$var" | tr ' ' _)
is not OK in most implementations (exceptions being yash
with ECHO_STYLE=raw
(with the caveat that yash
's variables can't hold arbitrary sequences of bytes so not arbitrary file names) and zsh
's echo -E - "$var"
).
printf
, on the other hand is more reliable, at least when it's limited to the basic usage of echo
.
printf '%sn' "$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 are a lot of extensions. For instance, some support a %q
to quote the arguments but how it's done varies from shell to shell, some support uxxxx
for unicode characters. The behavior varies for printf '%10sn' "$var"
in multi-byte locales, there are at least three different outcomes for printf %b '123'
But in the end, if you stick to the POSIX feature set of printf
and don't try doing anything too 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() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
The subshell (which implies spawning an extra process in most shell implementations) can be avoided using local IFS
with many shells, 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 of echo
(beside enable -n echo
or redefining echo
as a function or alias):
the xpg_echo
bash
option 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 "101"'
101% # the % here denotes the absence of newline character
xpg_echo
expands sequences as UNIX requires:
$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A
It still honours -n
and -e
(and -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%
With xpg_echo
and POSIX mode:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-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:
$ env 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 BSD3 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
3. BSD for echo -e?
The reference to BSD for the handling of the -e
option is a bit misleading here. Most of those different and incompatible echo
behaviours were all introduced at Bell labs:
n
,ooo
,c
in Programmer's Work Bench UNIX (based on Unix V6), and the rest (b
,r
...) in Unix System IIIRef.
-n
in Unix V7 (by Dennis RitchieRef)
-e
in Unix V8 (by Dennis RitchieRef)
-E
itself possibly initially came frombash
(CWRU/CWRU.chlog in version 1.13.5 mentions Brian Fox adding it on 1992-10-18, GNUecho
copying it shortly after in sh-utils-1.8 released 10 days later)
While the echo
builtin of the sh
or BSDs have supported -e
since the day they started using the Almquist shell for it in the early 90s, the standalone echo
utility to this day doesn't support it there (FreeBSD echo
still doesn't support -e
, though it does support -n
like Unix V7 (and also c
but only at the end of the last argument)).
The handling of -e
was added to ksh93
's echo
when in the BSD universe in the ksh93r version released in 2006 and can be disabled at compilation time.
4. macOS echo
Most versions of macOS have received UNIX certification from the OpenGroup.
Their sh
builtin echo
is compliant as it's bash
(a very old version) built with xpg_echo
enabled by default, but their stand-alone echo
utility is not. env echo -n
outputs nothing instead of -n<newline>
, env echo 'n'
outputs n<newline>
instead of <newline><newline>
.
That /bin/echo
is the one from FreeBSD which suppresses newline output if the first argument is -n
or (since 1995) if the last argument ends in c
, but doesn't support any other backslash sequences required by UNIX, not even \
. Which means it's not only difficult but impossible to have it output -n<newline>
or anythingc<newline>
in one invocation.
13
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 '14 at 7:57
6
As a note, the one (and maybe only) advantage of havingecho
expand thex
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (likeexecve()
) can't take arbitrary sequences of bytes)
– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, evenprintf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is inbosh
and the page from Sven Maschek does not list the problems with null bytes via.
– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
add a comment |
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 "nt"
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/versions chose to do it differently: they added a -e
option to 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
implementations 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"
with echo
in many shells.
On some shells like bash
1 or ksh93
2 or yash
($ECHO_STYLE
variable), the behavior even depends on how the shell was compiled or the environment (GNU echo
's behaviour will also change if $POSIXLY_CORRECT
is in the environment, zsh
's with its bsd_echo
option, some pdksh-based with their posix
option or whether they're called as sh
or not). So two bash
echo
s, 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. Even some certified systems like macOS4 are not compliant.
To really represent the current reality, POSIX should actually say: if the first argument matches the ^-([eEn]*|-help|-version)$
extended regexp or any argument contains backslashes (or characters whose encoding contains the encoding of the backslash character like α
in locales using the BIG5 charset), then the behaviour is unspecified.
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 compliant) echo
implementations like bash
's when the xpg_echo
option has not been enabled in one way or another like at compilation time or via the environment).
file=$(echo "$var" | tr ' ' _)
is not OK in most implementations (exceptions being yash
with ECHO_STYLE=raw
(with the caveat that yash
's variables can't hold arbitrary sequences of bytes so not arbitrary file names) and zsh
's echo -E - "$var"
).
printf
, on the other hand is more reliable, at least when it's limited to the basic usage of echo
.
printf '%sn' "$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 are a lot of extensions. For instance, some support a %q
to quote the arguments but how it's done varies from shell to shell, some support uxxxx
for unicode characters. The behavior varies for printf '%10sn' "$var"
in multi-byte locales, there are at least three different outcomes for printf %b '123'
But in the end, if you stick to the POSIX feature set of printf
and don't try doing anything too 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() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
The subshell (which implies spawning an extra process in most shell implementations) can be avoided using local IFS
with many shells, 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 of echo
(beside enable -n echo
or redefining echo
as a function or alias):
the xpg_echo
bash
option 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 "101"'
101% # the % here denotes the absence of newline character
xpg_echo
expands sequences as UNIX requires:
$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A
It still honours -n
and -e
(and -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%
With xpg_echo
and POSIX mode:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-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:
$ env 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 BSD3 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
3. BSD for echo -e?
The reference to BSD for the handling of the -e
option is a bit misleading here. Most of those different and incompatible echo
behaviours were all introduced at Bell labs:
n
,ooo
,c
in Programmer's Work Bench UNIX (based on Unix V6), and the rest (b
,r
...) in Unix System IIIRef.
-n
in Unix V7 (by Dennis RitchieRef)
-e
in Unix V8 (by Dennis RitchieRef)
-E
itself possibly initially came frombash
(CWRU/CWRU.chlog in version 1.13.5 mentions Brian Fox adding it on 1992-10-18, GNUecho
copying it shortly after in sh-utils-1.8 released 10 days later)
While the echo
builtin of the sh
or BSDs have supported -e
since the day they started using the Almquist shell for it in the early 90s, the standalone echo
utility to this day doesn't support it there (FreeBSD echo
still doesn't support -e
, though it does support -n
like Unix V7 (and also c
but only at the end of the last argument)).
The handling of -e
was added to ksh93
's echo
when in the BSD universe in the ksh93r version released in 2006 and can be disabled at compilation time.
4. macOS echo
Most versions of macOS have received UNIX certification from the OpenGroup.
Their sh
builtin echo
is compliant as it's bash
(a very old version) built with xpg_echo
enabled by default, but their stand-alone echo
utility is not. env echo -n
outputs nothing instead of -n<newline>
, env echo 'n'
outputs n<newline>
instead of <newline><newline>
.
That /bin/echo
is the one from FreeBSD which suppresses newline output if the first argument is -n
or (since 1995) if the last argument ends in c
, but doesn't support any other backslash sequences required by UNIX, not even \
. Which means it's not only difficult but impossible to have it output -n<newline>
or anythingc<newline>
in one invocation.
13
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 '14 at 7:57
6
As a note, the one (and maybe only) advantage of havingecho
expand thex
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (likeexecve()
) can't take arbitrary sequences of bytes)
– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, evenprintf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is inbosh
and the page from Sven Maschek does not list the problems with null bytes via.
– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
add a comment |
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 "nt"
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/versions chose to do it differently: they added a -e
option to 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
implementations 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"
with echo
in many shells.
On some shells like bash
1 or ksh93
2 or yash
($ECHO_STYLE
variable), the behavior even depends on how the shell was compiled or the environment (GNU echo
's behaviour will also change if $POSIXLY_CORRECT
is in the environment, zsh
's with its bsd_echo
option, some pdksh-based with their posix
option or whether they're called as sh
or not). So two bash
echo
s, 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. Even some certified systems like macOS4 are not compliant.
To really represent the current reality, POSIX should actually say: if the first argument matches the ^-([eEn]*|-help|-version)$
extended regexp or any argument contains backslashes (or characters whose encoding contains the encoding of the backslash character like α
in locales using the BIG5 charset), then the behaviour is unspecified.
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 compliant) echo
implementations like bash
's when the xpg_echo
option has not been enabled in one way or another like at compilation time or via the environment).
file=$(echo "$var" | tr ' ' _)
is not OK in most implementations (exceptions being yash
with ECHO_STYLE=raw
(with the caveat that yash
's variables can't hold arbitrary sequences of bytes so not arbitrary file names) and zsh
's echo -E - "$var"
).
printf
, on the other hand is more reliable, at least when it's limited to the basic usage of echo
.
printf '%sn' "$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 are a lot of extensions. For instance, some support a %q
to quote the arguments but how it's done varies from shell to shell, some support uxxxx
for unicode characters. The behavior varies for printf '%10sn' "$var"
in multi-byte locales, there are at least three different outcomes for printf %b '123'
But in the end, if you stick to the POSIX feature set of printf
and don't try doing anything too 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() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
The subshell (which implies spawning an extra process in most shell implementations) can be avoided using local IFS
with many shells, 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 of echo
(beside enable -n echo
or redefining echo
as a function or alias):
the xpg_echo
bash
option 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 "101"'
101% # the % here denotes the absence of newline character
xpg_echo
expands sequences as UNIX requires:
$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A
It still honours -n
and -e
(and -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%
With xpg_echo
and POSIX mode:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-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:
$ env 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 BSD3 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
3. BSD for echo -e?
The reference to BSD for the handling of the -e
option is a bit misleading here. Most of those different and incompatible echo
behaviours were all introduced at Bell labs:
n
,ooo
,c
in Programmer's Work Bench UNIX (based on Unix V6), and the rest (b
,r
...) in Unix System IIIRef.
-n
in Unix V7 (by Dennis RitchieRef)
-e
in Unix V8 (by Dennis RitchieRef)
-E
itself possibly initially came frombash
(CWRU/CWRU.chlog in version 1.13.5 mentions Brian Fox adding it on 1992-10-18, GNUecho
copying it shortly after in sh-utils-1.8 released 10 days later)
While the echo
builtin of the sh
or BSDs have supported -e
since the day they started using the Almquist shell for it in the early 90s, the standalone echo
utility to this day doesn't support it there (FreeBSD echo
still doesn't support -e
, though it does support -n
like Unix V7 (and also c
but only at the end of the last argument)).
The handling of -e
was added to ksh93
's echo
when in the BSD universe in the ksh93r version released in 2006 and can be disabled at compilation time.
4. macOS echo
Most versions of macOS have received UNIX certification from the OpenGroup.
Their sh
builtin echo
is compliant as it's bash
(a very old version) built with xpg_echo
enabled by default, but their stand-alone echo
utility is not. env echo -n
outputs nothing instead of -n<newline>
, env echo 'n'
outputs n<newline>
instead of <newline><newline>
.
That /bin/echo
is the one from FreeBSD which suppresses newline output if the first argument is -n
or (since 1995) if the last argument ends in c
, but doesn't support any other backslash sequences required by UNIX, not even \
. Which means it's not only difficult but impossible to have it output -n<newline>
or anythingc<newline>
in one invocation.
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 "nt"
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/versions chose to do it differently: they added a -e
option to 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
implementations 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"
with echo
in many shells.
On some shells like bash
1 or ksh93
2 or yash
($ECHO_STYLE
variable), the behavior even depends on how the shell was compiled or the environment (GNU echo
's behaviour will also change if $POSIXLY_CORRECT
is in the environment, zsh
's with its bsd_echo
option, some pdksh-based with their posix
option or whether they're called as sh
or not). So two bash
echo
s, 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. Even some certified systems like macOS4 are not compliant.
To really represent the current reality, POSIX should actually say: if the first argument matches the ^-([eEn]*|-help|-version)$
extended regexp or any argument contains backslashes (or characters whose encoding contains the encoding of the backslash character like α
in locales using the BIG5 charset), then the behaviour is unspecified.
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 compliant) echo
implementations like bash
's when the xpg_echo
option has not been enabled in one way or another like at compilation time or via the environment).
file=$(echo "$var" | tr ' ' _)
is not OK in most implementations (exceptions being yash
with ECHO_STYLE=raw
(with the caveat that yash
's variables can't hold arbitrary sequences of bytes so not arbitrary file names) and zsh
's echo -E - "$var"
).
printf
, on the other hand is more reliable, at least when it's limited to the basic usage of echo
.
printf '%sn' "$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 are a lot of extensions. For instance, some support a %q
to quote the arguments but how it's done varies from shell to shell, some support uxxxx
for unicode characters. The behavior varies for printf '%10sn' "$var"
in multi-byte locales, there are at least three different outcomes for printf %b '123'
But in the end, if you stick to the POSIX feature set of printf
and don't try doing anything too 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() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
The subshell (which implies spawning an extra process in most shell implementations) can be avoided using local IFS
with many shells, 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 of echo
(beside enable -n echo
or redefining echo
as a function or alias):
the xpg_echo
bash
option 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 "101"'
101% # the % here denotes the absence of newline character
xpg_echo
expands sequences as UNIX requires:
$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A
It still honours -n
and -e
(and -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%
With xpg_echo
and POSIX mode:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-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:
$ env 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 BSD3 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
3. BSD for echo -e?
The reference to BSD for the handling of the -e
option is a bit misleading here. Most of those different and incompatible echo
behaviours were all introduced at Bell labs:
n
,ooo
,c
in Programmer's Work Bench UNIX (based on Unix V6), and the rest (b
,r
...) in Unix System IIIRef.
-n
in Unix V7 (by Dennis RitchieRef)
-e
in Unix V8 (by Dennis RitchieRef)
-E
itself possibly initially came frombash
(CWRU/CWRU.chlog in version 1.13.5 mentions Brian Fox adding it on 1992-10-18, GNUecho
copying it shortly after in sh-utils-1.8 released 10 days later)
While the echo
builtin of the sh
or BSDs have supported -e
since the day they started using the Almquist shell for it in the early 90s, the standalone echo
utility to this day doesn't support it there (FreeBSD echo
still doesn't support -e
, though it does support -n
like Unix V7 (and also c
but only at the end of the last argument)).
The handling of -e
was added to ksh93
's echo
when in the BSD universe in the ksh93r version released in 2006 and can be disabled at compilation time.
4. macOS echo
Most versions of macOS have received UNIX certification from the OpenGroup.
Their sh
builtin echo
is compliant as it's bash
(a very old version) built with xpg_echo
enabled by default, but their stand-alone echo
utility is not. env echo -n
outputs nothing instead of -n<newline>
, env echo 'n'
outputs n<newline>
instead of <newline><newline>
.
That /bin/echo
is the one from FreeBSD which suppresses newline output if the first argument is -n
or (since 1995) if the last argument ends in c
, but doesn't support any other backslash sequences required by UNIX, not even \
. Which means it's not only difficult but impossible to have it output -n<newline>
or anythingc<newline>
in one invocation.
edited 14 mins ago
answered Feb 22 '13 at 21:31
Stéphane Chazelas
299k54563913
299k54563913
13
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 '14 at 7:57
6
As a note, the one (and maybe only) advantage of havingecho
expand thex
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (likeexecve()
) can't take arbitrary sequences of bytes)
– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, evenprintf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is inbosh
and the page from Sven Maschek does not list the problems with null bytes via.
– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
add a comment |
13
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 '14 at 7:57
6
As a note, the one (and maybe only) advantage of havingecho
expand thex
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (likeexecve()
) can't take arbitrary sequences of bytes)
– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, evenprintf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is inbosh
and the page from Sven Maschek does not list the problems with null bytes via.
– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
13
13
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 '14 at 7:57
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 '14 at 7:57
6
6
As a note, the one (and maybe only) advantage of having
echo
expand the x
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (like execve()
) can't take arbitrary sequences of bytes)– Stéphane Chazelas
Aug 4 '15 at 12:15
As a note, the one (and maybe only) advantage of having
echo
expand the x
sequences as opposed to the shell as part of the quoting syntax is that you can then output a NUL byte (another arguably mis-design of Unix is those null-delimited strings, where half the system calls (like execve()
) can't take arbitrary sequences of bytes)– Stéphane Chazelas
Aug 4 '15 at 12:15
As you may see on the web page from Sven Maschek, even
printf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is in bosh
and the page from Sven Maschek does not list the problems with null bytes via
.– schily
Jul 10 at 10:01
As you may see on the web page from Sven Maschek, even
printf
seems to be a problem since most implementations do it wrong. The only correct implementation I am aware of is in bosh
and the page from Sven Maschek does not list the problems with null bytes via
.– schily
Jul 10 at 10:01
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
This is a great answer - thanks @StéphaneChazelas for writing it up.
– JoshuaRLi
Dec 17 at 4:00
add a comment |
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 "%sn" "$vech"
Sources:
- http://bash.cyberciti.biz/guide/Echo_Command
- man echo
- man printf
@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
7
Usingecho
to print a variable can fail if the value of the variable contains meta-characters.
– Keith Thompson
Apr 29 '13 at 15:22
add a comment |
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 "%sn" "$vech"
Sources:
- http://bash.cyberciti.biz/guide/Echo_Command
- man echo
- man printf
@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
7
Usingecho
to print a variable can fail if the value of the variable contains meta-characters.
– Keith Thompson
Apr 29 '13 at 15:22
add a comment |
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 "%sn" "$vech"
Sources:
- http://bash.cyberciti.biz/guide/Echo_Command
- man echo
- man printf
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 "%sn" "$vech"
Sources:
- http://bash.cyberciti.biz/guide/Echo_Command
- man echo
- man printf
edited May 13 at 11:31
GAD3R
25.4k1750106
25.4k1750106
answered Feb 22 '13 at 20:13
NlightNFotis
4,48452437
4,48452437
@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
7
Usingecho
to print a variable can fail if the value of the variable contains meta-characters.
– Keith Thompson
Apr 29 '13 at 15:22
add a comment |
@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
7
Usingecho
to print a variable can fail if the value of the variable contains meta-characters.
– Keith Thompson
Apr 29 '13 at 15:22
@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
@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
7
7
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
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
add a comment |
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 "somenmulti-linedntextn"
(NB: the last n
is necessary, echo
implies it, unless you give the -n
option)
versus
echo -e "somenmulti-linedntext"
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
.
1
True for/usr/bin/echo
and thebash
builtin. Thedash
,ksh
andzsh
builtinecho
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
2
@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
2
Or you could just doprintf '%sn' 'foobar
.
– nyuszika7h
Mar 9 '14 at 17:36
|
show 3 more comments
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 "somenmulti-linedntextn"
(NB: the last n
is necessary, echo
implies it, unless you give the -n
option)
versus
echo -e "somenmulti-linedntext"
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
.
1
True for/usr/bin/echo
and thebash
builtin. Thedash
,ksh
andzsh
builtinecho
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
2
@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
2
Or you could just doprintf '%sn' 'foobar
.
– nyuszika7h
Mar 9 '14 at 17:36
|
show 3 more comments
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 "somenmulti-linedntextn"
(NB: the last n
is necessary, echo
implies it, unless you give the -n
option)
versus
echo -e "somenmulti-linedntext"
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
.
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 "somenmulti-linedntextn"
(NB: the last n
is necessary, echo
implies it, unless you give the -n
option)
versus
echo -e "somenmulti-linedntext"
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
.
answered Feb 22 '13 at 20:21
0xC0000022L
7,3421564117
7,3421564117
1
True for/usr/bin/echo
and thebash
builtin. Thedash
,ksh
andzsh
builtinecho
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
2
@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
2
Or you could just doprintf '%sn' 'foobar
.
– nyuszika7h
Mar 9 '14 at 17:36
|
show 3 more comments
1
True for/usr/bin/echo
and thebash
builtin. Thedash
,ksh
andzsh
builtinecho
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
2
@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
2
Or you could just doprintf '%sn' 'foobar
.
– nyuszika7h
Mar 9 '14 at 17:36
1
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
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
Why the scare quotes? Your wording implies that it isn't necessarily a real advantage.
– Keith Thompson
Apr 29 '13 at 15:26
2
2
@KeithThompson: actually all they are meant to imply is that not everyone may consider it an advantage.
– 0xC0000022L
Apr 29 '13 at 15:33
@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
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
2
2
Or you could just do
printf '%sn' 'foobar
.– nyuszika7h
Mar 9 '14 at 17:36
Or you could just do
printf '%sn' 'foobar
.– nyuszika7h
Mar 9 '14 at 17:36
|
show 3 more comments
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
.
10
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 somepdksh
derivatives.
– Stéphane Chazelas
Nov 7 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to useprintf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.
– schily
Jul 3 at 14:46
add a comment |
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
.
10
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 somepdksh
derivatives.
– Stéphane Chazelas
Nov 7 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to useprintf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.
– schily
Jul 3 at 14:46
add a comment |
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
.
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
.
edited Oct 3 '14 at 17:05
HalosGhost
3,70592235
3,70592235
answered Oct 3 '14 at 17:00
John
6911
6911
10
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 somepdksh
derivatives.
– Stéphane Chazelas
Nov 7 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to useprintf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.
– schily
Jul 3 at 14:46
add a comment |
10
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 somepdksh
derivatives.
– Stéphane Chazelas
Nov 7 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to useprintf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.
– schily
Jul 3 at 14:46
10
10
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 '14 at 13:52
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 '14 at 13:52
But many of those printf implementations are broken. This is essential when you like to use
printf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.– schily
Jul 3 at 14:46
But many of those printf implementations are broken. This is essential when you like to use
printf
to output nul bytes, but some implementations interpret `c' in the format string even though they should not.– schily
Jul 3 at 14:46
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f65803%2fwhy-is-printf-better-than-echo%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown