什么可以解释下面的示例以及如何解决这个问题,最好不要大量引用杂技?我正在使用 来$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
看不到争论,因此不会搞砸。