在Bash中的命令中单引号内的变量扩展


393

我想从bash脚本运行一个命令,该脚本具有单引号,并且在单引号内还有一些其他命令和一个变量。

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

在这种格式下,$将转义,并且变量不会扩展。

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

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

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

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

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

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

请告诉我我要去哪里错了。


33
repo forall -c ' ...before... '"$variable"' ...after...'
n。代词

2
@nm单引号是repo命令的一部分。我认为这不会起作用
Rachit 2012年

1
bash吃单引号。您不在in中bash,或者单引号不属于repo命令的一部分。
n。代词

不知道我能帮到您,但是万一需要单引号,就不会在-c“'... ...之前...” $ variable“ ...之后...'”工作吗?
ecv

它在bash shell中运行的bash脚本和repo forall命令在单引号内包含参数。如果我用实际值代替,则命令运行正常。
Rachit

Answers:


621

在单引号内,所有内容均按原样保留,无一例外。

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

'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,这意味着它是按字面意义使用的,没有变量扩展或任何其他形式的解释。


@ Jo.SO将不起作用。因为repo命令将只接受参数,直到关闭第一个引号为止。之后的任何内容都将被回购忽略,并生成另一个错误。
拉奇(Rachit)2012年

8
@Rachit-外壳不能那样工作。 "some"thing' like'this只是外壳一词而已。引号不就解析而言终止任何东西,也没有办法一个名为命令通过 shell来告诉一下被引述如何。
马克·里德

3
第二句(“您甚至无法转义单引号”)使单引号成为Shell的黑洞。

@Evert:不知道怎么说更好。我删除了这句话。
乔苏(So So

@JoSo这太可惜了:没有一个笑话平淡无奇。

96

repo命令不在乎它得到什么样的引号。如果需要参数扩展,请使用双引号。如果那意味着您不得不反斜杠很多内容,请对大多数内容使用单引号,然后将其括起来,并在需要扩展的部分加倍。

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

如果您感兴趣,请按照以下说明进行操作。

从外壳运行命令时,该命令作为参数接收的是一个以null终止的字符串数组。这些字符串可以绝对包含任何非空字符。

但是,当外壳程序通过命令行构建字符串数组时,它会特别解释某些字符。这是为了使命令更容易(实际上是可能的)输入。例如,空格通常表示数组中字符串之间的边界。因此,有时将各个参数称为“单词”。但是,论点中可能还有空格。您只需要一些方法告诉外壳程序就是您想要的。

您可以在任何字符(包括空格或其他反斜杠)前面使用反斜杠,以指示外壳按字面意义对待该字符。但是,尽管您可以执行以下操作:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

...可能会很累。因此,外壳提供了另一种选择:引号。这些有两个主要品种。

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

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

单引号更加严格。它们之间的所有内容都完全按字面意义使用,包括反斜杠。绝对没有办法在单引号内获取文字单引号。

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

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

这样比较容易-反斜杠要少得多,尽管右斜杠单引号,单斜杠单反引号,开路单引号的顺序需要一些时间来适应。

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

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

需要注意的重要一点是,在所有这些示例中,作为echo命令参数接收的单个字符串是完全相同的。Shell完成命令行解析后,就无法运行命令来告诉引用的内容。即使它想要。


2
是。我现在知道了。它运作完美。非常感谢你的帮助!
拉奇(Rachit)2012年

6
这个答复真棒。
维尼修斯Ferrão

1
$'string'格式符合POSIX标准?另外,有名字吗?
通配符

2
@Wildcard $'string'是非POSIX扩展名,我将其称为“ ANSI字符串”;我已经将这些事实纳入了答案。此类字符串可在大多数现代的Bourne兼容外壳程序中使用:bash,dash,ksh(AT&T和PD)以及zsh均支持它们。
马克·里德

1
回链接- 命令行参数中需要转义哪些字符?在Unix和Linux堆栈交换上。
通配符

3

以下是对我有用的-

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

2
我希望TBL_HDFS_DIR_PATH不是用户提供的输入顺便说一句,这将允许一个不错的sql注入...
domenukk 17-11-3

1
QUOTE在一个单独的变量完全是多余的; 双引号内的单引号只是一个常规字符。(此外,请勿对您的私有变量使用大写字母。)
Tripleee

2

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

从那时起,我一直在研究这个问题。我很幸运能够回购信息。我仍然不清楚是否需要强制将命令括在单引号之间。我调查了repo语法,但我认为您不需要这样做。您可以在命令周围使用双引号,然后在内部使用所需的任何单引号和双引号,只要您避免使用双引号即可。


感谢Kevin的帮助。
拉奇(Rachit)2012年

2

只需使用printf

代替

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

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

例如:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")

这坏了;printf在这里并不能真正为您买任何东西,并且如果不能用引号代替命令会引起新的引号问题。
三人

0

变量可以包含单引号。

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

repo forall -c $myvar

他们可以,但是这不是正确的方法-您仍应"$myvar"使用双引号。
点钟

-2

这对您有用吗?

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

8
它适合您吗?这个问题已有5年历史了,已经有了一些答案,甚至是一个可以接受的答案……
Nico Haase
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.