My colleague use Ubuntu and I use openSUSE, we compiled same source code using same makefile, my environment works well, but my colleague can't, always output the can't recognized -e option. We check the makefile, ONLY find echo command use -e option.
Dose Ubuntu's echo function is different with others?
+++++update
in makefile, the echo define as:
TOOLSDIR =
ECHO = $(TOOLSDIR)echo -e
The $(TOOLSDIR) is dir for tool, this make file can detect compile env, if linux or has CYGWIN:
$(TOOLSDIR)is empty, if windows, it will goes to WIN32 version echo tool dir, like:
TOOLSDIR = $(subst /,\,$(MAKEDIR)/tools/WIN32/)
after we execute make, ubuntu will output:
Fatal error: L3900U: Unrecognized option '-e'.
however, openSUSE doesn't have this error
+++++update for echo -e
I write a test makefile in ubuntu
all:
echo -e hello
it output:
echo -e hello
hello
+++++update makefile test in openSUE (12.1) and ubuntu (12.04)
all:
echo -e "hello 1"
echo -e hello 2
echo -e he\nllo3
echo -e "he\nllo4"
opensuse, the output is:
echo -e "hello 1"
hello 1
echo -e hello 2
hello 2
echo -e he\nllo3
henllo3
echo -e "he\nllo4"
he
llo4
in ubuntu, the output is:
echo -e "hello 1"
-e hello 1
echo -e hello 2
hello 2
echo -e he\nllo3
henllo3
echo -e "he\nllo4"
-e he
llo4
Meanwhile, i test in ubuntu, echo without -e, the result is same as in openSUSE, echo with -e
It could depend from the shell you guys are using (echo is often implemented inside the shell)
this is what happens in OS X (Mountain Lion):
$ bash
bash-4.2$ echo -e foo
foo
bash-4.2$ sh
sh-3.2$ echo -e foo
-e foo
The same for OpenSUSE, and Ubuntu.
-e option is not compliant to POSIX (I'm not sure, but it should be the default behavior), btw, to print formatted text, printf is more appropriate command.
From the information you provide, it looks to me that your makefile has a bug (portability issue).
On OSX man echo doesn't list the -e option too, btw. On Ubuntu 12.10 the -e is present on my computer, on my router BusyBox v1.13.4 also accepts the -e (it's internal command for busybox)
But probably it's just the /bin/sh using internal command and ignoring it.
==== UPDATE:
In your makefile remove the $(TOOLSDIR) in front of echo:
ECHO = echo -e
in fact if you specify the path of the echo (i.e. /bin/echo) you force the shell to execute the program from the filesystem instead of the one implemented internally by the shell:
$ echo -e foo
foo
$ /bin/echo -e foo
-e foo
I also find it may cause by ubuntu link /bin/sh to dash but NOT bash. After I use bash, problem solved and there is an other question related to this, which I asked:
same shell script has different behaviou on different Linux distribution
Both Luigi R. Viggiano and How Chen provide good explanations to the problem and solutions to it. In fact, their answers complement each other.
In dash, the echo command does not accept the -e flag. However, in bash, it does.
In Ubuntu, one might consider using update-alternatives to select bash, rather than dash, as the default shell. The following commands will do the trick (warning, these commands will apply system-wide):
sudo update-alternatives --install /bin/sh sh /bin/bash 0
sudo update-alternatives --install /bin/sh sh /bin/dash 0
sudo update-alternatives --set sh /bin/bash
The \ is the escape character in the shell, so \n is just that, an "n", so escape the escape \\\n.
[sg#Study ~]$ echo -e \\nfoo\\nfoo
foo
foo
[sg#Study ~]$
or use quotes
[sg#Study ~]$ echo -e "\\nfoo\\nfoo"
foo
foo
[sg#Study ~]$
Related
interactive programs can typically read input from stdin, e.g.,
$ echo echo hello | bash
hello
or
$ echo 1 2 + p | dc
3
however, nix-shell does not seem to behave this way, e.g.
$ echo hello | nix-shell -p hello
$
whereas the expected output would have been Hello, world!.
using the trick suggested in nix-shell(1):
--command cmd
In the environment of the derivation, run the shell command cmd. This command is executed in an
interactive shell. (Use --run to use a non-interactive shell instead.) However, a call to exit is
implicitly added to the command, so the shell will exit after running the command. To prevent
this, add return at the end; e.g. --command "echo Hello; return" will print Hello and then drop
you into the interactive shell. This can be useful for doing any additional initialisation.
leads to an error:
$ echo hello | nix-shell -p hello --command return
/tmp/nix-shell-15399-0/rc: line 1: return: can only `return' from a function or sourced script
$
my versions of the relevant programs are these:
$ nix --version
nix (Nix) 2.3.2
$ bash --version
GNU bash, version 4.4.23(1)-release (x86_64-pc-linux-gnu)
$
hence my question: how do i make nix-shell read from stdin, like bash or dc?
Unlike its name may suggest, nix-shell isn't a shell in the typical UNIX sense of the word; only in the broader sense that it's a program for starting programs.
The -p arguments only serve to bring software into the shell's environment. You can use multiple and they don't have the effect of actually running those programs*.
To make nix-shell start bash, regardless of nix-shell's own logic, use for example:
$ echo hello | nix-shell -p hello --run bash
Hello, world!
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.)
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...
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.
I am trying to do something like this
ruby test.rb | source /dev/stdin
where test.rb just prints out cd /. There are no errors, but it doesn't do anything either. If I use this:
ruby test.rb > /tmp/eraseme2352; source /tmp/eraseme2352
it works fine, but I want to avoid the intermediate file.
Edit: The whole point of this is that the changes need to persist when the command is done. Sorry I didn't make that clearer earlier.
You can try:
$(ruby test.rb)
$(...) tells bash to execute whatever output is produced by command inside ().
eval `ruby test.rb`
The following code works for me on Mac OS X 10.6.7:
/bin/bash
sw_vers # Mac OS X 10.6.7
bash --version # GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
source /dev/stdin <<<'echo a b c'
source /dev/stdin <<< "$(ruby -e 'puts "echo a b c"')"
source /dev/stdin <<<'testvar=TestVal'; echo $testvar
source /dev/stdin <<-'EOF'
echo a b c
EOF
source /dev/stdin <<-'EOF'
$(ruby -e 'puts "echo a b c"')
EOF
(
source /dev/stdin <<-'EOF'
testvar=TestVal
EOF
echo $testvar
)
Until a more experienced bash hacker comes along to correct me, you could do this:
for c in `ruby test.rb` ; do $c ; done
Caution: This doesn't do what you want. Read the comments!
bash (not sh):
while read -a line
do
"${line[#]}"
done < <(somescript)
Spaces in arguments to commands will need to be backslash-escaped in order to work.
How about just
ruby test.rb | bash