views:

102

answers:

2

When running a bash script on ubuntu 9.10, I get different behavior from bash echo's "-e" option depending on whether or not I'm running as root.

Consider this script:

$ cat echo-test
if [ "`whoami`" = "root" ]; then
  echo "Running as root" 
fi
echo Testing /bin/echo -e
/bin/echo -e "foo\nbar"
echo Testing bash echo -e
echo -e "foo\nbar"

When run as non-root user, I see this output:

$ ./echo-test 
Testing /bin/echo -e
foo
bar
Testing bash echo -e
foo
bar

When run as root, I see this output:

$ sudo ./echo-test 
Running as root
Testing /bin/echo -e
foo
bar
Testing bash echo -e
-e foo
bar

Notice the "-e" being echoed in the last case ("-e foo" instead of "foo" on the second-to-last line). When running a script as root, the echo command runs as if "-e" was given and, if -e is given, the option itself is echoed.

I can understand some subtle differences in behavior between /bin/echo and bash echo, but I would expect bash echo to behave the same no matter which user invokes it.

Anyone know why this is the case? Is this a bug in bash echo?

*FYI -- I'm running GNU bash, version 4.0.33(1)-release (x86_64-pc-linux-gnu)*

+2  A: 

are you sure that your user and root both use bash as their shell? try putting a "shebang" line (#!/bin/bash) in your script as first line, and run it again

zed_0xff
#!/bin/bash at the top fixes it. So root must be using a different shell with a different echo. That makes sense.Thank you!
+1  A: 

It might be interesting to see the value of POSIXLY_CORRECT environment variable and to determine whether xpg_echo shell option is enabled in both situations.

if test -n "${POSIXLY_CORRECT+yes}"; then
    pc="set '$POSIXLY_CORRECT'"
else
    pc=unset
fi
echo POSIXLY_CORRECT: "$pc"
shopt -q xpg_echo && xe=set || xe=unset
echo xpg_echo: $xe

Here is the test code I used to examine the various combinations:

{ n=1
for p in '' p; do
    for x in '' x; do
        for e in '' e; do
            printf "\nmode: ${p:-_}${x:-_}${e:-_}\n"
            test -n "$x" && xx=-s || xx=-u
            bash ${p:+--posix} -c "              shopt $xx xpg_echo
              test -n \"\${POSIXLY_CORRECT+yes}\" && pc=\"set '\$POSIXLY_CORRECT'\" || pc=unset
              shopt -q xpg_echo && xe=set || xe=unset
              echo POSIXLY_CORRECT: \"\$pc\"
              echo xpg_echo: \$xe
              echo${e:+ -e} \"$n\n$((n+1))\"
            "
            n=$((n+2))
        done
    done
done
}

On my system, your “as root” effect is reproduce in the last case (both POSIXLY_CORRECT and xpg_echo are set).

mode: ___
POSIXLY_CORRECT: unset
xpg_echo: unset
1\n2

mode: __e
POSIXLY_CORRECT: unset
xpg_echo: unset
3
4

mode: _x_
POSIXLY_CORRECT: unset
xpg_echo: set
5
6

mode: _xe
POSIXLY_CORRECT: unset
xpg_echo: set
7
8

mode: p__
POSIXLY_CORRECT: set 'y'
xpg_echo: unset
9\n10

mode: p_e
POSIXLY_CORRECT: set 'y'
xpg_echo: unset
11
12

mode: px_
POSIXLY_CORRECT: set 'y'
xpg_echo: set
13
14

mode: pxe
POSIXLY_CORRECT: set 'y'
xpg_echo: set
-e 15
16

These variations in the behavior are the primary reason the use of printf is advocated over echo.

Depending on the system and shell, sometimes printf is a shell built-in command and sometimes it is an external command, but its behavior is generally much more consistent than that of echo.

You can be sure that each of the following commands will produce an embedded newline and no trailing newline:

printf 'foo\nbar'
printf '%s\n%s' foo bar

BTW, this shows my preferred style of using single quotes for the format string to indicate that nothing funky is going into it (e.g. a parameter expansion that inserts an extra format specifier that is not reflected in the argument list).

Chris Johnsen