Why don't "echo -e" commands seem to produce the right output?
In the terminal of Ubuntu 18.04 LTS, when I write this command:
echo -e "Hello\\\n"
It gives this as the output:
Hello\n
But it should print, as the man page says:
Hello\
That is, first print Hello
, then \
, then a newline character (and then another newline). Where is the problem?
See these few echo -e
commands and their output:
man420@man420-X542UQ:~$ echo -e "HEllo\a\a\a\a\a\a\\\n"
HEllo\n
man420@man420-X542UQ:~$ echo -e "HEllo\\\n"
HEllo\n
man420@man420-X542UQ:~$ echo -e "HEllo\a\\\n"
HEllo\n
Backslashes are treated specially by echo -e
, but first they are sometimes treated specially by the shell (which in this case is bash
), according to the shell's quoting rules.
The argument echo
actually sees is Hello\\n
. Because you have passed -e
to echo
, it treats backslash escapes specially, and \\
is collapsed to a single \
. The final n
is not escaped, so it appears literally.
The reason for this is that, prior to and separately from the operation of echo
itself, the shell treats \
specially in some contexts but not others. Unquoted \
characters are always treated specially, single-quoted \
are never treated specially, but the treatment of double-quoted \
characters, as in the command you ran, is more subtle and complicated.
When the shell encounters double-quoted text in a command, as with "Hello\\\n"
, it treats \
as an escape character when it precedes a character that can itself have special meaning inside double quotes and not otherwise.
- Because
\
sometimes has a special meaning inside""
, a\
that immediately precedes another\
has the effect of quoting that second\
. So inside double quotes, the first\\
are collapsed into\
. - After those first two
\
characters, there is a third\
character which is unaffected by those first two and which precedes ann
. Butn
is not a character that is ever treated specially inside double quotes, so the\
before it is not treated specially in this situation. Thus\n
stays\n
.
The effect is that, in double quotes, \\\n
is interpreted as \\n
.
When echo -e
sees \\n
, the first \
removes special meaning from the second, so echo
prints \n
literally for that text.
If your goal is to print Hello
with an extra newline but no backslashes (the question was originally ambiguous about this, plus I think it is a useful example), then one solution is to remove a \
. Running echo -e "Hello\\n"
outputs Hello
with an extra newline at the end.
A better solution is to use single quotes and write echo -e 'Hello\n'
. Single quotes are the strongest form of quoting. An even better solution is usually to use printf
instead of echo
, which in this case would be printf 'Hello\n\n'
.
If your goal is to print Hello\
including the backslash, followed by an extra newline, then the double-quoted approach is possible but cumbersome. To figure it out, work backwards: echo -e
converts \\
to \
and \n
to a newline, and double-quoting converts \\
to \
, so you can do it with six backslashes (echo -e "Hello\\\\\\n"
), because \\n
turns into \n
due to one backslash escaping another. You can also do it with five, because double-quoting retains \n
when the \
is not escaped.
This illustrates the benefit of single quoting: just put whatever you want echo -e
to see inside the single quotes (echo -e 'Hello\\\n'
). printf
is also good here. One option is printf 'Hello\\\n\n'
. But where printf
really shines is that you can plug in additional arguments. You can use printf '%s\n\n' 'Hello\'
, which inserts the second argument (literally Hello\
because single quotes don't change anything) in place of %s
.