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

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

例如:# EYZ0

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

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

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


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


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


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

如果我将值替换为变量,命令就会很好地执行。

请告诉我我哪里做错了。

552800 次浏览

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

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

'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时使用了单个勾号,这意味着它是字面上的,没有变量展开或任何其他形式的解释。

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

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

如果你感兴趣,下面是解释。

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

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

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

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

...这会让人感到厌烦。因此shell提供了另一种选择:引号。这些主要有两种。

双引号被称为“分组引号”。它们防止展开通配符和别名,但主要用于在单词中包含空格。其他诸如参数和命令扩展(由$表示的类型)之类的事情仍然会发生。当然,如果你想在双引号内加上双引号,你必须反斜杠:

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 string”。还有$''对"ANSI quotes"在这样的字符串中,上面关于从字面上理解反斜杠的建议不再适用。相反,它们又变得特别了——不仅可以通过在它前面加上一个反斜杠来包含一个文字单引号或反斜杠,而且shell还扩展了ANSI C字符转义(例如\n用于换行,\t用于制表符,\xHH用于十六进制代码HH的字符)。然而,在其他情况下,它们表现为单引号字符串:不会发生参数或命令替换:

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

需要注意的重要一点是,在所有这些示例中,存储在reply变量中的单个字符串都是完全一样。类似地,在shell解析完命令行之后,运行的命令无法确切地告诉每个参数字符串实际上是如何键入的——甚至无法告诉它是如何键入的如果,而不是以某种编程方式创建的。

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

从那以后我一直在调查这件事。我很幸运,手头有回购。我仍然不清楚你是否需要把你的命令用单引号括起来。我研究了回购语法,我认为你不需要。您可以在命令周围使用双引号,然后在命令内部使用任何您需要的单引号和双引号,前提是要转义双引号。

下面是对我有效的方法

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

这对你有用吗?

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

变量可以包含单引号。

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


repo forall -c $myvar

只需使用printf

而不是

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

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

例如:

template='.... %s'


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

我想知道为什么我永远不能让我的awk语句从ssh会话打印,所以我找到了这个论坛。这里没有什么直接帮助我,但如果有人有类似的问题,然后给我一个投票。似乎任何单引号或双引号都没有帮助,但我并没有尝试所有的方法。

check_var="df -h / | awk 'FNR==2{print $3}'"

Getckvar =$(ssh user@host "$check_var")

echo $ getckvar

你得到了什么?一无所有。

修复:逃脱\$3,在您的打印功能。