bash 如何处理嵌套引号? [复制]
Posted
技术标签:
【中文标题】bash 如何处理嵌套引号? [复制]【英文标题】:How does bash deal with nested quotes? [duplicate] 【发布时间】:2014-11-14 11:45:17 【问题描述】:我需要运行如下语法的命令:runuser -l userNameHere -c '/path/to/command arg1 arg2'
不幸的是,我必须在命令本身中嵌套额外的'
字符,我无法告诉 bash 正确解释这些字符。我想运行的命令实际上是:
runuser -l miner -c 'screen -S Mine -p 0 -X eval 'stuff "pwd"\015''
不幸的是,bash 似乎正在击中第二个 '
并呕吐。这是错误:-bash: screen -S Mine -p 0 -X eval stuff: No such file or directory
,所以显然它没有超过'
。
如何将其嵌套为一个命令?谢谢!
【问题讨论】:
命令eval 'stuff "pwd"\015'
是什么? (或者更确切地说,我知道eval
是什么,但它在评估什么?)
先生,它是gnu-screen.
的一部分,这会将短语pwd
传递到屏幕上。
【参考方案1】:
您可以使用bash
、$'...'
支持的其他类型的引用。这可以包含转义的单引号。
runuser -l miner $'screen -S Mine -p 0 -X eval \'stuff "pwd"\015\''
请注意,在$'...'
中,\015
将被替换为代码点 015 处的实际 ASCII 字符,因此如果这不是您想要的,您还需要转义反斜杠。
runuser -l miner $'screen -S Mine -p 0 -X eval \'stuff "pwd"\\015\''
我认为您也可以利用$'...'
来消除对eval
的需求:
runuser -l miner $'screen -S Mine -p 0 -X stuff "pwd"\015'
【讨论】:
很好,我不知道你可以用$''
做到这一点【参考方案2】:
命令本身看不到外引号。 shell 使用这些来避免分词,但不会让命令知道它们在那里。所以如果你有:
./command foo bar "baz biz"
该命令看到三个 args,foo、bar 和 baz biz,空格完整,但没有引号。因此,如果您需要实际发送引号,您可以通过使用另一种引号包装参数来做到这一点:
./command "foo'bar"
该命令看到一个 arg:foo'bar。但是,如果您需要发送两种 种报价,您将面临更难解决的问题。你可以用倾斜的牙签、引用交换或变量来解决它:
报价交换
即使 shell 使用引号来避免分词,如果您将带引号的参数彼此相邻而没有空格,命令会将其视为一个单词:
./command "foo""bar"
该命令看到一个 arg:foobar。因此,如果您使用两种不同的引号:
./command 'foo"bar'"baz'quux"
命令看到一个arg:foo"barbaz'quux。
倾斜的牙签
有两种倾斜牙签。一个实际上只是引号交换,除非您不使用引号来包装其中一个…引号。
./command 'foo"barbaz'\'quux
该命令看到一个 arg:foo"barbaz'quux。另一个是(帽子提示:chepner)使用特殊的 $'string'
字扩展形式,它允许 ANSI C 字符串:
./command $'foo"barbaz\'quux'
命令看到一个arg:foo"barbaz'quux.
变量
doublequote=\" singlequote=\'
./command "foo$doublequotebarbaz$singlequotequux"
命令看到一个arg:foo"barbaz'quux.
【讨论】:
【参考方案3】:您不能在单引号内嵌套单引号。您可以通过转义双引号将双引号嵌套在双引号中。
使用单引号可以做的最好的事情是在需要嵌入单引号的地方使用'\''
。
那是单引号结束单引号字符串。未引用但已转义的单引号。和一个单引号开始下一个单引号字符串。
【讨论】:
【参考方案4】:除了手动转义之外,您还可以让 shell 为您执行此操作并避免麻烦(如果您的 shell 是 bash
;这是 POSIX sh 中不可用的扩展,因此您的 shebang 需要为 @987654323 @,而不是 #!/bin/sh
)。
例如:
printf -v command '%q ' /path/to/command arg1 arg2
printf -v outer_command '%q ' runasuser -l userNameHere -c "$command"
printf -v ssh_command '%q ' ssh hostname "$outer_command"
...将在"$ssh_command"
中放入一个字符串,如果对其进行评估,它将在runasuser
内部运行ssh
内部的内部命令。
提供一些如何使用这些变量的示例,一旦构建:
# this evaluates the command in the current shell in a sane way
ssh hostname "$outer_command"
# this evaluates the command in the current shell in an inefficient way
eval "$ssh_command"
# this evaluates the command in a new shell
bash -c "$ssh_command"
# this evaluates the command in a new shell as a different user
sudo -u username bash -c "$ssh_command"
# this writes the command to a script, and makes it executable
printf '%s\n' "#!/bin/bash" "$ssh_command" >run_ssh
chmod +x run_ssh
提供一些反例说明如何不在构造后使用这些变量:
# DO NOT DO THESE: WILL NOT WORK
$ssh_command # see http://mywiki.wooledge.org/BashFAQ/050
"$ssh_command" # likewise
sudo "$ssh_command" # likewise
【讨论】:
这正是我想要的。不幸的是它不起作用:printf -v command '%q ' /usr/bin/date +'%F %X'; printf -v outer_command '%q ' sudo -u root "$command"...
产生错误:bash: line 2: sudo -u root /usr/bin/date\ +%F\\\ %X\ : No such file or directory
@Hubbitus,是的,sudo -u root "$command"
不是正确的调用。另一方面,sudo -u root bash -c "$command"
可以正常工作。 (请注意,它的间接层数比没有充分的理由要多,但我认为您试图证明一个观点)。
@Hubbitus, ...这是真的,因为 sudo
将它的 argv 数组直接传递给它的孩子(在解析了前导参数之后),而 ssh
(这里的示例代码是为它构建的) 将其参数连接成一个字符串,它通过该字符串传递给远程eval
。
让我们continue this discussion in chat。
@Hubbitus,上面链接的聊天会话中的解释有帮助吗?【参考方案5】:
查看我的回答here。基本上,您不能将单引号放在单引号内 - 但您不必这样做,因为引号不是单词分隔符,因此您可以使用关闭的 '
跳出引号,现在添加文字单引号你在 \'
的引号之外,然后用新的打开的 '
回到引号中,所有这些都不会终止当前的 shell 字。
【讨论】:
以上是关于bash 如何处理嵌套引号? [复制]的主要内容,如果未能解决你的问题,请参考以下文章