Why doesn't echo -n work in shell on Mac? - bash

The man page for echo says:
-n Do not print the trailing newline character. This may also be
achieved by appending `\c' to the end of the string, as is done by
iBCS2 compatible systems. Note that this option as well as the
effect of `\c' are implementation-defined in IEEE Std 1003.1-2001
(``POSIX.1'') as amended by Cor. 1-2002. Applications aiming for
maximum portability are strongly encouraged to use printf(1) to
suppress the newline character.
However this doesn't seem to work in sh on Mac:
sh-3.2$ echo $0
/bin/sh
sh-3.2$ which echo
/bin/echo
sh-3.2$ echo -n foo
-n foo
It works properly in bash:
bash-3.2$ echo $0
bash
bash-3.2$ which echo
/bin/echo
bash-3.2$ echo -n foo
foobash-3.2
FWIW this only seems to happen on Mac, on Linux it works properly:
$ echo $0
sh
$ echo -n foo
foo$

-n is a bash extension to echo. In version 3.2 (which ships with macOS), bash does not support the extension when invoked as sh. Starting with version 4.0 (some version of which is likely on your Linux box), bash does honor -n when invoked as sh.
Update: the xpg_echo option determines if bash's built-in echo is POSIX-compliant or not. In bash 3.2 (or at least the macOS build of 3.2), this option defaults to on; in bash 4.x, it defaults to off.
% sh -c 'shopt xpg_echo'
xpg_echo on
% ./sh -c 'shopt xpg_echo'
xpg_echo off
(./sh is a symlink to /usr/local/bin/bash, a local installation of bash 4.4 on my machine.)

Related

Strange behaviour for echo with -e flag passed to bash with -c flag

I cannot understand the behaviour of this bash script (which I cut it out of a longer real use case):
# This is test.sh
cmd="echo -e \"\n\n\n\t===== Hello World =====\n\n\""
sh -c "$cmd"
What it prints is:
$ ./test.sh
-e
===== Hello World =====
$
If I remove the -e flag, everything is printed correctly, with quoted chars correctly interpreted and without the '-e' spoil: but it shouldn't be like that.
My bash is: GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17), under macOS.
In Posix mode (when run as sh), bash 3.2's echo command takes no options; -e is just another argument to write to standard output. Compare:
$ bash -c 'echo -e "a\tb"'
a b
$ sh -c 'echo -e "a\tb"'
-e a b
A literal tab is printed in both cases because Posix echo behaves the same as bash echo -e.
For this reason, printf is almost always better to use than echo to provide consistent behavior.
cmd='printf "\n\n\n\t===== Hello World =====\n\n"'
sh -c "$cmd"
sh-4.2# cat test.sh
cmd="echo -e \"\n\n\n\t===== Hello World =====\n\n\""
sh -c "$cmd"
sh-4.2# ./test.sh
===== Hello World =====
sh-4.2#
It is getting printed correctly on my machine
OK, I think I found it myself, from here:
sh, the Bourne shell, is old. Its behaviour is specified by the POSIX standard. If you want new behaviour, you use bash, the Bourne Again shell, which gets new features added to it all the time. On many systems, sh is just bash, and bash turns on a compatibility mode when run under that name.
Groan...

echo flag -n is printing out when run from script [duplicate]

How come sh UsersInput.sh gives a different output compared to bash UsersInput.sh?
My script is below:
#!/bin/bash
echo -n "Enter: ";
read usersinput;
echo "You entered, \"$usersinput\"";
bash
localhost:Bash henry$ bash UsersInput.sh
Enter: input
You entered, "input"
sh
localhost:Bash henry$ sh UsersInput.sh
-n Enter:
input
You entered, "input"
How come -n behaves properly with the first, but not with the second? What's the reason for this and is there a workaround?
From man echo:
Some shells may provide a builtin echo command which is similar or identical to this utility. Most notably, the builtin echo in sh(1) does not accept the -n option. Consult the builtin(1) manual page.
In bash, the Bourne-again shell, echo accepts the -n option whereas in sh, the Bourne shell, echo does not, so it simply echos everything you wrote, including the -n.
/bin/sh is a version of bash (not a Bourne shell) on OS X. It has POSIX mode enabled and has a few other changes as well. One of them is that the xpg_echo shell option is enabled by default so that the builtin echo conforms to POSIX.
http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html:
Implementations shall not support any options
http://www.gnu.org/software/bash/manual/bash.html#Bash-POSIX-Mode:
44. When the xpg_echo option is enabled, Bash does not attempt to interpret any arguments to echo as options. Each argument is displayed, after escape characters are converted.
[...]
As noted above, Bash requires the xpg_echo option to be enabled for the echo builtin to be fully conformant.
You can unset xpg_echo, use /bin/echo, or preferably just use printf:
sh -c 'shopt -u xpg_echo; echo -n aa'
sh -c '/bin/echo -n aa'
sh -c 'printf %s aa'

Bash parameter expansion works only in interactive shell, but not in script

In user's console I have bash:
$ echo $SHELL
/bin/bash
$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)
I have code in file test.sh:
$ cat test.sh
aaa='---aa-aa---'
echo "${aaa}"
echo 'does not work...'
# trim "-"
echo ${aaa/+(-)}
echo ${aaa%%+(-)}
echo 'works for one symbol...'
echo ${aaa%-}
echo ${aaa/-}
The last two rows work fine but previous ones.
$ bash test.sh
---aa-aa---
does not work...
---aa-aa---
---aa-aa---
works for one symbol...
---aa-aa--
--aa-aa---
In the same time, if you would try to make this console it works:
$ aaa='---aa-aa---'
$ echo ${aaa/+(-)}
aa-aa---
$ echo ${aaa%%+(-)}
---aa-aa
So, why it doesn't work in a script?
You seem to have shopt -s extglob enabled in your interactive shell, which turns on extended globbing. This is not the default behavior, and needs to be explicitly enabled in your script. See extended pattern matching in the bash hackers wiki for details.

Bash vs. Dash behavior with the command `echo -ne "hello\n"`

I got different behaviors with the same command echo -ne "hello\n" with bash and with dash. See below :
$ bash -c 'echo -ne "hello\n"'
hello
$ dash -c 'echo -ne "hello\n"'
-ne hello
Why is that ? I don't understand at all…
My system :
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 12.04.5 LTS
Release: 12.04
Codename: precise
The POSIX specification for echo doesn't support any arguments. See it here.
And while the spec mentions -n it does so to say that it is not an option and is either an implementation defined case or to be treated as a string.
So dash is doing exactly that.
bash, on the other hand, has non-conforming behavior in a number of ways.
This is why the use of echo is generally discouraged in favor of the using printf which has a much better specification and much better portable behavior.
While echo implementation in bash is not POSIX and Unix conformed by default, you can alter its behavior at run time or compile time.
At run time, with xpg_echo and in POSIX mode, bash echo became conformant:
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -ne "hello\n"'
-ne hello
or:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT= bash -c 'echo -ne "hello\n"'
-ne hello
At compile time, you can pass --enable-xpg-echo-default and --enable-strict-posix-default options to configure script.

xargs echo colored output

I have the following command:
somethingRegex | xargs -I {} sh -c 'echo -e "found \e[34m{}\e[39m";dummy {}'
The color part of the echo does not work, example output:
-e found \e[34mresult\e[39m
dummy output
repeat
A plain echo does work with {} being nice blue
echo -e "found \e[34m{}\e[39m"
How do I fix this?
Perhaps this is for Linux (although OSX adds an interesting twist by reversing the roles of bash and echo).
Linux's /bin/echo has a -e option which expands escapes of the sort you show, while some shells (such as dash, used in Debian) follow POSIX more closely, and do not do this. The find program acts as if it runs /bin/sh, which may not be your actual shell. Debian uses dash as /bin/sh.
Likewise, older versions of bash (my local OSX server has 3.2.53) do not support the -e option, while newer ones (checking my local Debian with 4.1.5) do support the -e option.
Since all of that behavior is non-standard, the usual recommendation is to use the printf utility, which also provides non-standard features on Linux, but the parts that you need will be portable enough:
somethingRegex | xargs -I {} sh -c 'printf "found \033[34m{}\033[39m";dummy {}'

Resources