繁体   English   中英

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

[英]Expansion of variables inside single quotes in a command in Bash

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

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

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

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

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

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

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

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

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

请告诉我我哪里出错了。

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

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

'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 这就是您想要的。

您可以在任何字符(包括空格或另一个反斜杠)前使用反斜杠来告诉 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 字符串”和$' ... '对“ANSI 引号”。 在这些字符串中,上述关于从字面上采用反斜杠的建议不再适用。 相反,它们再次变得特别——您不仅可以通过在其前面添加反斜杠来包含文字单引号或反斜杠,而且 shell 还扩展了 ANSI C 字符转义(如\\n表示换行符, \\t表示制表符,以及\\xHH表示具有十六进制代码HH的字符)。 然而,否则,它们表现为单引号字符串:不发生参数或命令替换:

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

需要注意的重要一点是,在所有这些示例中,存储在reply变量中的单个字符串完全相同 同样,外壳做解析命令行后,就没有办法为正在运行的命令,告诉每一个参数字符串中究竟是如何实际键入-或即使它是类型化的,而不是创建程序莫名其妙。

以下是对我有用的内容-

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

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

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

只需使用 printf

代替

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

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

例如:

template='.... %s'

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

变量可以包含单引号。

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

repo forall -c $myvar

这对你有用吗?

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM