echo and printf :: Bash
Intro
Note
For all the examples that follow, set the
|
echo
echo and newline
$ echo -n
Why does echo -n
still produce a newline and the next prompt is on a
line of its own?
It is not echo
that is producing a newline. Because we first add a
newline by hitting Enter (a.k.a Return) in order to execute the
command, then bash prints nothing (we provided nothing for echo
to
print), the output is a newline from our Enter and nothing else. The
$
prompt is positioned on a line of its own simply because echo
had
nothing to print.
When we do echo -n foo
and hit Enter, we first produce a newline,
then bash prints 'foo'.
$ echo -n foo<Return>
foo$
Then the prompt $
is positioned immediately after 'foo'. After all, we
asked echo
NOT to append a newline, so, echo
prints 'foo' and the
prompt is positioned right after `echo’s output.
How to print '-n'?
If we just do echo -n
, the -n
is treated as the -n
option (do not
append a newline).
This doesn’t work:
$ echo -- -n
-- -n.
Not what we want…
We learned in End of Options that a anything following --
should be treated as a normal string operand, and not as an option to the program.
Why then is --
not working here and preventing -n
from beting treaded an option‽
Because bash’s echo
honors the specs:
The echo utility shall not recognize the “--” argument in the manner specified by Guideline 10 of XBD Utility Syntax Guidelines; “--” shall be recognized as a string operand.
— echo POSIX spec
We can man ascii
and look for the numeric value of --
:
Excerpt from `man ascii’.
Oct Dec Hex Char
──────────────────────
...
055 45 2D -
...
Then we can use the -e
option for echo
and use the octal or
hexadecimal values to produce -
and just implicitly concatenate both
-
and n
.
$ echo -e '\055'n
-n
$ echo -e '\x2d'n
-n
It has been said that:
"Any fool can make something complicated. It takes a genius to make it simple.”
Therefore:
$ echo -n -; echo n;
Jokes apart, the version with -e
and \x2d
is cool and useful too. It
is nice to have the tools and know how to use them.
Nice question and discussion: When and how was the double-dash (--) introduced as an end of options delimiter in Unix/Linux?
Prefer printf instead of echo
The use of echo
is discouraged for several reasons. First, see
echo
application usage.
Basically, behaviour differs across implementations making it all but
impossible to use echo
in a reliable and portable way.
Also, observe the output of these commands:
$ var=-e
$ echo "$var"
Nothing is printed. 😮
$ arr=(-e -n -en -ne)
$ echo "${arr[@]}"
Same problem… But we are fine with printf
:
$ var=-e
$ printf '%s\n' "$var"
-e
$ arr=(-e -n -en -ne)
$ printf '%s\n' "${arr[@]}"
-e
-n
-en
-ne
However, these work with echo
:
$ var=-e
$ echo "hello $var"
hello -e
$ arr=(-e -n -en -ne)
$ printf 'hello %s\n' "${arr[@]}"
hello -e
hello -n
hello -en
hello -ne
As do these:
$ echo " $var"
-e
$ printf ' %s\n' "${arr[@]}"
-e
-n
-en
-ne
In bash’s echo
at least, we can print those option-like parameters
as long as there is something before them. Even a whitespace before them
causes it to work. But do note that the space is preserved in the
output.
Well, the options are there, and echo
can still be used for certain
things, but care must be taken.
printf
Contrary to echo
, printf
does not add a newline by default.
$ printf '%s' hello
hello$
$ printf '%s\n' hello
hello
$
Format operand reutilization
Another thing to consider is that the format operand (%s
, %d
,
etc.) is reused until all argument operands are consumed:
"The format operand shall be reused as often as necessary to satisfy the argument operands."
That explains why even with a single %s
, the next line prints all
argument operands (instead of just the first one):
$ printf '%s\n' may the force
may
the
force
$ words=(be with you)
$ printf '%s\n' "${words[@]}"
be
with
you