为什么“sudo -i”而不是“sudo”删除换行符?

为什么“sudo -i”而不是“sudo”删除换行符?

什么可以解释下面的示例以及如何解决这个问题,最好不要大量引用杂技?我正在使用 来$n模拟多行命令字符串,以防它分散您对真正问题的注意力。

~$ n=$'\n'; sudo -i echo "line1${n}line2${n}"
line1line2
~$

~$ n=$'\n'; sudo echo "line1${n}line2${n}"
line1
line2

~$

答案1

在 strace 下运行sudo -i echo $'line1\nline2'显示 Bash 的启动方式如下:

9183  execve("/bin/bash", ["-bash", "--login", "-c", "echo line1\\\nline2\\\n"], ...

现在,strace当显示字符串时,会用反斜杠转义来显示特殊字符,因此 Bash 实际获取的参数-c是,对于 shell,行尾的反斜杠标记了连续行,并删除了反斜杠和后面的换行符。echo line1[backslash][newline]line2[backslash][newline]

没有-i,直接sudo运行echo,不经过 shell:

9189  execve("/bin/echo", ["echo", "line1\nline2\n"], ... 

在这里,这是一个字面上的换行符echo,并将echo其打印出来。


这里的想法一定是sudo尝试添加一层外壳转义以适应以下事实:sh -c需要单身的字符串,而sudo其本身将命令作为不同的参数。

比较以下案例:

sudo转义空格(这只是命令的名称,没有参数!):

$ sudo -i 'echo foo'
-bash: echo foo: command not found

sudo转义反斜杠,因此这实际上有效(Bashecho不处理反斜杠):

$ sudo -i echo 'foo\bar'
foo\bar

与选项卡相同:

$ sudo -i echo $'foo\tbar'
foo     bar

这里,反斜杠上没有额外的引号,因此 Bash 在处理 shell 命令行时将其删除(b对于 shell 来说不是特殊字符,不需要引号。这与 基本相同bash -c 'echo foo"b"ar'):

$ bash -c 'echo foo\bar'
foobar

问题在于你无法用反斜杠转义换行符,并且sudo似乎没有考虑到这一点。

无论如何,如果您将所需的命令存储在文件中并将其作为脚本运行,那么引用此类问题可能会变得容易一些。

答案2

您可以更改命令的结构:

echo "echo \"line1${n}line2${n}\"" | sudo -i bash -s

这种方式sudo看不到争论,因此不会搞砸。

相关内容