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

时间:2012-12-10 11:10:14

标签: bash shell variables quotes

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

e.g。 repo forall -c '....$variable'

在此格式中,$被转义,变量未展开。

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

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

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

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

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

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

请告诉我哪里出错了。

7 个答案:

答案 0 :(得分:490)

在单引号内,所有内容都是字面上保留的,无一例外。

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

'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的赋值中使用单个刻度,这意味着它是字面上的,没有可变扩展或任何其他形式的解释。

答案 1 :(得分:81)

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换行,{{1对于制表符,\t表示具有十六进制代码\xHH的字符。否则,它们表现为单引号字符串:不进行参数或命令替换:

HH

需要注意的重要一点是,作为echo $'"Thank you. That\'ll be $4.96, please," said the cashier' 命令的参数接收的单个字符串在所有这些示例中都是完全相同的。在shell完成解析命令行之后,没有办法运行命令来告诉引用的内容。即使它想要。

答案 2 :(得分:2)

编辑:(根据相关评论:)

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

答案 3 :(得分:0)

以下是对我有用的东西 -

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

答案 4 :(得分:0)

变量可以包含单引号。

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

repo forall -c $myvar

答案 5 :(得分:0)

只需使用printf

代替

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

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

例如:

template='.... %s'

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

答案 6 :(得分:-2)

这对你有用吗?

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