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,foobarbaz 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 如何处理嵌套引号? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在电源查询中加载 CSV 时如何处理多个引号?

Java:想要解析JSON,但是遇到好多双引号,该如何处理

如何处理 Postgresql 查询中的单引号 [重复]

使用 python sqlite3 模块,如何处理包含双引号的字段?

如何在 R 中显示带引号的文本? [复制]

什么是JSON+如何处理JSON字符串