Reputation: 1270
In bash, in this specific case, echo behaves like so:
$ bash -c 'echo "a\nb"'
a\nb
but in zsh the same thing turns out very differently...:
$ zsh -c 'echo "a\nb"'
a
b
and fwiw in fish, because I was curious:
$ fish -c 'echo "a\nb"'
a\nb
I did realize that I can run:
$ zsh -c 'echo -E "a\nb"'
a\nb
But now I am worried that I'm about to stumble into more gotchas on such a basic operation. (Thus my investigation into fish: if I'm going to have to make changes at such a low level for zsh, why not go all the way and switch up to something that is blatant about being so different?)
I did not myself find any documentation to help clarify this echo difference in bash vs zsh or pages directly listing the differences, so can someone here list them out? And maybe direct me to any broader set of potentially impactful gotchas when making the switch, that would cover this case?
Upvotes: 4
Views: 2023
Reputation: 107
echo provides the same output, regardless of the shell interpreter it's called from.
The difference lies in the way that each shell will print the standard output buffer to the screen. zsh interprets escape characters/sequences (i.e., "\n\t\v\r..." or ANSI escape sequences) automatically where bash does not.
Using bash, you'll have to supply the -e flag to print newlines:
#Input:
echo "[text-to-print]\n[text-to-print-on-newline]"
#Output:
[text-to-print]\n[text-to-print-on-newline]
#Input:
echo -e "[text-to-print]\n[text-to-print-on-newline]"
#Output:
[text-to-print]
[text-to-print-on-newline]
Using zsh, the interpreter does the escape sequence interpretation by itself, and the output is the same regardless of the -e flag:
#Input:
echo "[text-to-print]\n[text-to-print-on-newline]"
#Output:
[text-to-print]
[text-to-print-on-newline]
#Input:
echo -e "[text-to-print]\n[text-to-print-on-newline]"
#Output:
[text-to-print]
[text-to-print-on-newline]
This should fix the discrepancy that you're seeing between shell interpreters.
Upvotes: 1
Reputation: 19615
Usually prefer printf
for consistent results.
If you need predictable consistent echo
implementation, you can override it with your own function.
This will behave the same, regardless of the shell.
echo(){ printf %s\\n "$*";}
echo "a\nb"
Upvotes: 4
Reputation: 12518
echo
is good and portable only for printing literal strings that end
with a newline but it's not good for anything more complex, you can
read more about it in this
answer and in
Shellcheck documentation
here.
Even though according to
POSIX
each implementation has to understand character sequences without any
additional options you cannot rely on that. As you already noticed, in
Bash for example echo 'a\nb'
produces a\nb
but it can be changed
with xpg_echo
shell option:
$ echo 'a\nb'
a\nb
$ shopt -s xpg_echo
$ echo 'a\nb'
a
b
And maybe direct me to any broader set of potentially impactful gotchas when making the switch, that would cover this case?
Notice that the inconsistency between different echo
implementations
can manifest itself not only in shell but also in other places where
shell is used indirectly, for example in Makefile. I've once come
across Makefile that looked like this:
all:
@echo "target\tdoes this"
@echo "another-target\tdoes this"
make
uses /bin/sh to run these commands so if /bin/sh is a symlink
to bash on your system what you get is:
$ make
target\tdoes this
another-target\tdoes this
If you want portability in shell use printf
. This:
printf 'a\nb\n'
should produce the same output in most shells.
Upvotes: 4