546

我想从一个bash 脚本运行一个命令,该脚本在单引号和一个变量中包含单引号和一些其他命令。

例如repo forall -c '....$variable'

在这种格式中,$被转义并且变量不被扩展。

我尝试了以下变体,但被拒绝了:

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

如果我用值代替变量,则命令执行得很好。

请告诉我哪里出错了。

4

7 回答 7

842

在单引号内,所有内容都按字面意思保留,无一例外。

这意味着您必须关闭引号,插入一些内容,然后再次重新输入。

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

单词连接只是通过并置完成。如您所见,上述每一行对 shell 来说都是一个单词。引号(单引号或双引号,视情况而定)不会隔离单词。它们仅用于禁用对各种特殊字符的解释,如空格、$...;有关引用的好教程,请参阅 Mark Reed 的答案。也相关:哪些字符需要在 bash 中转义?

不要连接由 shell 解释的字符串

您绝对应该避免通过连接变量来构建 shell 命令。这是一个类似于连接 SQL 片段(SQL 注入!)的坏主意。

通常可以在命令中包含占位符,并将命令与变量一起提供,以便被调用者可以从调用参数列表中接收它们。

例如,以下是非常不安全的。不要这样做

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

如果 的内容$myvar不受信任,这是一个漏洞利用:

myvar='foo"; echo "you were hacked'

代替上面的调用,使用位置参数。下面的调用更好——它是不可利用的:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

请注意在对 的赋值中使用了单个刻度script,这意味着它是按字面意思进行的,没有变量扩展或任何其他形式的解释。

于 2012-12-10T14:00:30.803 回答
121

repo命令不在乎它得到什么样的引号。如果需要参数扩展,请使用双引号。如果这意味着你最终不得不反斜杠很多东西,对大部分内容使用单引号,然后将它们分开并在需要扩展的部分使用双引号。

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

解释如下,如果你有兴趣。

当您从 shell 运行命令时,该命令作为参数接收的是一个以 null 结尾的字符串数组。这些字符串可能绝对包含任何非空字符。

但是当 shell 从命令行构建字符串数组时,它会专门解释一些字符;这旨在使命令更容易(实际上,可能)键入。例如,空格通常表示数组中字符串之间的边界;出于这个原因,个别论点有时被称为"词"。但是一个论点可能仍然有空格。你只需要某种方式来告诉 shell 这就是你想要的。

您可以在任何字符(包括空格或另一个反斜杠)前面使用反斜杠来告诉 shell 按字面意思处理该字符。但是,虽然您可以执行以下操作:

reply=\”That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

......它可能会让人厌烦。所以shell提供了一个替代方案:引号。这些有两个主要品种。

双引号称为“分组引号”。它们防止通配符和别名被扩展,但主要是为了在单词中包含空格。参数和命令扩展等其他事情(由 a 表示的那种事情$)仍然会发生。当然,如果您想在双引号内使用文字双引号,则必须将其反斜杠:

reply="\"That'll be \$4.96, please,\" said the cashier"

单引号更严厉。它们之间的所有内容都完全按照字面意思理解,包括反斜杠。绝对没有办法在单引号内获得文字单引号。

幸运的是,shell 中的引号不是单词分隔符;他们自己不会终止一个词。您可以在同一个单词中进出引号,包括不同类型的引号之间,以获得所需的结果:

reply='"That'\''ll be $4.96, please," said the cashier'

所以这更容易 - 更少的反斜杠,虽然关闭单引号,反斜杠文字单引号,打开单引号序列需要一些时间来适应。

现代 shell 添加了另一种 POSIX 标准未指定的引用样式,其中前导单引号以美元符号为前缀。如此引用的字符串遵循与 C 编程语言的 ANSI 标准版本中的字符串文字类似的约定,因此有时称为“ANSI 字符串”和$'...'对“ANSI 引号”。在这样的字符串中,上述关于反斜杠的建议不再适用。相反,它们再次变得特别 - 您不仅可以通过在其前面添加反斜杠来包含文字单引号或反斜杠,而且 shell 还扩展了 ANSI C 字符转义(如\n换行符、\t制表符和\xHH带有十六进制代码HH)。但是,否则,它们表现为单引号字符串:不会发生参数或命令替换:

reply=$'"That\'ll be $4.96, please," said the cashier'

需要注意的重要一点是,在所有这些示例中,存储在reply变量中的单个字符串完全相同。同样,在 shell 完成对命令行的解析之后,正在运行的命令无法准确地告诉每个参数字符串是如何实际输入的——或者即使是输入的,而不是以某种方式以编程方式创建的。

于 2012-12-11T12:02:20.847 回答
2

编辑:(根据有问题的评论:)

从那以后我一直在研究这个。我很幸运,我有回购协议。我仍然不清楚您是否需要强制将命令括在单引号之间。我查看了 repo 语法,我认为您不需要。你可以在你的命令周围使用双引号,然后使用你需要的任何单引号和双引号,只要你转义双引号。

于 2012-12-11T14:42:54.830 回答
2

以下是对我有用的 -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
于 2017-03-17T18:08:58.140 回答
2

只需使用 printf

代替

repo forall -c '....$variable'

使用 printf 将变量标记替换为扩展变量。

例如:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
于 2018-10-21T02:48:50.150 回答
0

变量可以包含单引号。

myvar=\'....$variable\'

repo forall -c $myvar
于 2018-06-24T01:52:14.377 回答
-3

这对你有用吗?

eval repo forall -c '....$variable'
于 2018-01-23T11:18:10.560 回答