Help in explaining this echo conundrum.

OSX 10.12.3, default bash terminal.
Consider this code and note it is calling 'sh' inside the code...

#!/bin/sh
echo '1\n2\n2\n3\n5' > /tmp/text
hexdump -C /tmp/text
/bin/echo '1\n2\n3\n4\n5' > /tmp/text
hexdump -C /tmp/text

Now view the interactive mode below, note the underlying shell is bash:-

Last login: Tue Mar 14 17:12:45 on ttys001
AMIGA:barrywalker~> # Use sh first...
AMIGA:barrywalker~> sh
AMIGA:barrywalker~> cd Desktop/Code/Shell
AMIGA:barrywalker~/Desktop/Code/Shell> echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:barrywalker~/Desktop/Code/Shell> hexdump -C /tmp/text
00000000  31 0a 32 0a 33 0a 34 0a  35 0a                    |1.2.3.4.5.|
0000000a
AMIGA:barrywalker~/Desktop/Code/Shell> /bin/echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:barrywalker~/Desktop/Code/Shell> hexdump -C /tmp/text
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:barrywalker~/Desktop/Code/Shell> ./echo_test.sh
00000000  31 0a 32 0a 32 0a 33 0a  35 0a                    |1.2.2.3.5.|
0000000a
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:barrywalker~/Desktop/Code/Shell> # Now exit sh...
AMIGA:barrywalker~/Desktop/Code/Shell> exit
exit
AMIGA:barrywalker~> bash -version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
AMIGA:barrywalker~> cd Desktop/Code/Shell
AMIGA:barrywalker~/Desktop/Code/Shell> echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:barrywalker~/Desktop/Code/Shell> hexdump -C /tmp/text
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:barrywalker~/Desktop/Code/Shell> /bin/echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:barrywalker~/Desktop/Code/Shell> hexdump -C /tmp/text
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:barrywalker~/Desktop/Code/Shell> ./echo_test.sh
00000000  31 0a 32 0a 32 0a 33 0a  35 0a                    |1.2.2.3.5.|
0000000a
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:barrywalker~/Desktop/Code/Shell> _

1) Why does the 'bash' interactive mode give a different result to the 'sh' mode as 'sh' is merely a subset of 'bash'?
2) Why does echo in 'sh' translate '\n' to newlines in single quotes?
I thought single quotes were to give a raw string of characters, irrespective?
(Is this an Apple OSX thing?)
The 'sh' version is the same as the 'bash' version but are NOT identical files.

TIA...

'echo' is not consistent or portable between different shells. Sometimes you get newlines, sometimes you get \n, sometimes you only get newlines when you ask for them with echo -e, and sometimes you can't get newlines out no matter what you do (i.e. /bin/sh on solaris). Given OSX's nextstep/mach lineage, I'll wild-guess that their /bin/sh is meant to resemble an old Bourne from BSD.

printf is a lot more consistent.

1 Like

Note that there is a reason that echo is not part of the POSIX specification. For consistent results, it is best to only use printf

sh-3.2$ /usr/bin/printf '1\n2\n2\n3\n5\n'
1
2
2
3
5
sh-3.2$ printf '1\n2\n2\n3\n5\n'
1
2
2
3
5

I get

sh-3.2$ echo '1\n2\n3\n4\n5' | od -bc
0000000   061 012 062 012 063 012 064 012 065 012                        
           1  \n   2  \n   3  \n   4  \n   5  \n                        
0000012
sh-3.2$ /bin/echo '1\n2\n3\n4\n5' | od -bc
0000000   061 134 156 062 134 156 063 134 156 064 134 156 065 012        
           1   \   n   2   \   n   3   \   n   4   \   n   5  \n        
0000016

So in the first case \n is interpreted as newline, in the second case it is not.

--
Interestingly:

When I do:
/bin/bash --posix , which is supposed to be the same as /bin/sh I get:

bash-3.2$ echo '1\n2\n3\n4\n5' | od -bc
0000000   061 134 156 062 134 156 063 134 156 064 134 156 065 012        
           1   \   n   2   \   n   3   \   n   4   \   n   5  \n        
0000016

Also when bash is invoked without the --posix option

On OSX, apparently it is not a link to /bin/bash as is the case on some Linux distributions, but rather they are two distinct binaries

sh-3.2$ ls -l /bin/sh /bin/bash
-r-xr-xr-x  1 root  wheel  626272 Sep 14 02:57 /bin/bash
-r-xr-xr-x  1 root  wheel  630464 Sep 14 02:57 /bin/sh

If I make a symlink sh -> /bin/bash

$ ./sh
sh-3.2$ echo '1\n2\n3\n4\n5' | od -bc
0000000   061 134 156 062 134 156 063 134 156 064 134 156 065 012        
           1   \   n   2   \   n   3   \   n   4   \   n   5  \n        
0000016
1 Like

@ Scrutinizer...

Also interestingly when using 'dash' on OSX 10.12.3 default bash terminal:-

Last login: Tue Mar 14 18:32:20 on ttys000
AMIGA:amiga~> /usr/local/bin/dash
AMIGA:\u\w> echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:\u\w> hexdump -C /tmp/text
00000000  31 0a 32 0a 33 0a 34 0a  35 0a                    |1.2.3.4.5.|
0000000a
AMIGA:\u\w> /bin/echo '1\n2\n3\n4\n5' > /tmp/text
AMIGA:\u\w> hexdump -C /tmp/text
00000000  31 5c 6e 32 5c 6e 33 5c  6e 34 5c 6e 35 0a        |1\n2\n3\n4\n5.|
0000000e
AMIGA:\u\w> exit
AMIGA:amiga~> _

Seems consistent with 'sh'.
I am going to have to be careful with this.

---------- Post updated at 07:39 PM ---------- Previous update was at 07:10 PM ----------

I do know a few ideas but do not know if this would work in the Solaris situation you quoted.
This is longhand using 'dash' and '/bin/echo'...
You will have to take my word for the 'bold' as it does not copy and paste too well... ;o)

Last login: Tue Mar 14 19:22:37 on ttys000
AMIGA:amiga~> /usr/local/bin/dash
AMIGA:\u\w> nl='
> '
AMIGA:\u\w> esc=`printf "\033"`
AMIGA:\u\w> /bin/echo '1\n2\n3'
1\n2\n3
AMIGA:\u\w> /bin/echo "1\n2\n3"
1\n2\n3
AMIGA:\u\w> /bin/echo '1${nl}2${nl}3'
1${nl}2${nl}3
AMIGA:\u\w> /bin/echo "1${nl}2${nl}3"
1
2
3
AMIGA:\u\w> # Make characters BOLD...
AMIGA:\u\w> /bin/echo "Normal text, $esc[1mbold text..."
Normal text, bold text...
AMIGA:\u\w> exit
AMIGA:amiga~> _

There are ways to bend rules even in dash...

Basically, only use echo if you want to print preformatted / unescaped data: Any extended behavior like processing backslashes isn't portable.

I don't see any point fighting it when there's better alternatives though. Your solution is twice as complicated and much less efficient than just using printf once to do both jobs.
printf "\033[1mBold Text\033[0m Normal Text\n"

You're not breaking the rules, though :slight_smile: It's completely fine to store any ASCII character besides NULL in a variable.

Hi Corona688...

Perhaps but my non-rooted Android phone does NOT have printf it only has the same as my echo but the terminal I have on it does emulate the escape codes and the shell resembles a cut down version of bash, probably 'bash --posix' as I can only call it as 'sh'. Hence my experiments with 'echo' over a period of time.

Then it's not actually a Bourne shell, and your efforts to be portable with it will be in vain.

If it's not rooted, you're using some weird imitation cooked up in Java and likely don't even have a terminal.