Why does `sudo -i` but not `sudo` remove newline characters?
Running that sudo -i echo $'line1\nline2'
under strace shows Bash gets started like this:
9183 execve("/bin/bash", ["-bash", "--login", "-c", "echo line1\\\nline2\\\n"], ...
Now, strace
presents special characters with backslash-escapes when it displays the strings, so what Bash actually gets as the argument to -c
is echo line1[backslash][newline]line2[backslash][newline]
and for the shell, a backslash at the end of a line marks a continuation line and removes the backslash and the following newline.
Without -i
, sudo
runs echo
directly, without going through the shell:
9189 execve("/bin/echo", ["echo", "line1\nline2\n"], ...
Here, that's a literal newline going to echo
, and echo
duly prints that.
The idea here must be that sudo
tries to add a layer of shell escaping to accommodate for the fact that sh -c
takes a single string, while sudo
itself takes the command as distinct arguments.
Compare the following cases:
sudo
escapes the space (this is just the name of the command, no arguments!):
$ sudo -i 'echo foo'
-bash: echo foo: command not found
sudo
escapes the backslash, so that this actually works (Bash's echo
doesn't process the backslash):
$ sudo -i echo 'foo\bar'
foo\bar
Same with a tab:
$ sudo -i echo $'foo\tbar'
foo bar
Here, there's no extra quoting on the backslash, so Bash removes it while processing the shell command line (b
isn't a special character to the shell, and doesn't need quoting. This is basically the same as bash -c 'echo foo"b"ar'
):
$ bash -c 'echo foo\bar'
foobar
The problem is just that you can't escape a newline with a backslash, and sudo
doesn't seem to take that into account.
In any case, quoting issues like this probably turn quite a bit easier if you store the commands you want in a file, and run that as a script.
You could change the structure of the command:
echo "echo \"line1${n}line2${n}\"" | sudo -i bash -s
This way sudo
does not see the argument and thus cannot mess it up.