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

问题描述 投票:307回答:7

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

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

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

我尝试了以下变化,但他们被拒绝了:

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

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

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

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

如果我用值代替变量来执行命令就好了。

请告诉我哪里出错了。

bash shell variables quotes
7个回答
509
投票

在单引号内,一切都是字面上保留,毫无例外。

这意味着你必须关闭引号,插入一些东西,然后再次重新输入。

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

单词连接简单地通过并置来完成。您可以验证,上面的每一行都是shell的单个单词。引号(单引号或双引号,视情况而定)不会隔离单词。它们仅用于禁用各种特殊字符的解释,如空格,$; ......有关引用的好教程请参阅Mark Reed的答案。也相关:Which characters need to be escaped in 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的赋值中使用单个刻度,这意味着它是字面上的,没有可变扩展或任何其他形式的解释。


80
投票

repo命令不关心它得到什么样的引用。如果需要参数扩展,请使用双引号。如果这意味着你不得不反复使用大量的东西,那么对大多数东西使用单引号,然后突破它们并进入双打,以便你需要进行扩展。

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

如果您有兴趣,请说明如下。

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

但是当shell从命令行构建那个字符串数组时,它会特别解释一些字符;这旨在使命令更容易(实际上,可能)键入。例如,空格通常表示数组中字符串之间的边界;因此,个别论点有时被称为“单词”。但是,一个论点可能会有空格;你需要一些方法来告诉shell你想要什么。

您可以在任何字符(包括空格或其他反斜杠)前面使用反斜杠来告诉shell按字面意思处理该字符。但是你可以这样做:

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

......它可能会让人厌倦。因此shell提供了另一种选择:引号。这有两个主要品种。

双引号称为“分组引号”。它们可以防止通配符和别名被扩展,但主要是它们用于包含单词中的空格。其他一些事情,比如参数和命令扩展(由$发出的各种事情)仍然会发生。当然,如果你想在双引号内使用双字引号,你必须反斜杠:

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

单引号更严苛。它们之间的所有东西都是字面意思,包括反斜杠。绝对没有办法在单引号内获得文字单引号。

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

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

所以这更容易 - 反斜杠的数量要少得多,尽管单引号,反向单引号,单引号,开放单引号序列需要一些人习惯。

现代shell添加了另一种POSIX标准未指定的引用样式,其中前导单引号以美元符号为前缀。引用的字符串遵循与ANSI C编程语言中的字符串文字类似的约定,因此有时称为“ANSI字符串”,$' ... '对“ANSI引号”。在这些字符串中,上面关于反斜杠的建议实际上不再适用。相反,它们再次变得特殊 - 不仅可以通过在其前面添加反斜杠来包含文字单引号或反斜杠,而且shell还会扩展ANSI C字符转义(如换行符为\n,tab为\t\xHHHH)带有十六进制代码的字符echo $'"Thank you. That\'ll be $4.96, please," said the cashier' )。否则,它们表现为单引号字符串:不进行参数或命令替换:

echo

需要注意的重要一点是,作为repo forall -c '....$variable' 命令的参数接收的单个字符串在所有这些示例中都完全相同。在shell完成解析命令行之后,没有办法运行命令来告诉引用的内容。即使它想要。


2
投票

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

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


1
投票

只需使用printf

代替

template='.... %s'

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

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

例如:

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

repo forall -c $myvar

0
投票

变量可以包含单引号。

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

0
投票

以下是对我有用的 -

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

-2
投票

这对你有用吗?

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.