提问人:Rachit 提问时间:12/10/2012 最后编辑:codeforesterRachit 更新时间:7/30/2023 访问量:628360
在 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
评论
"some"thing' like'this
该命令不在乎它得到什么样的报价。如果需要参数扩展,请使用双引号。如果这意味着你最终不得不反切很多东西,那么大部分内容都使用单引号,然后从中分离出来,在需要扩展的部分变成双引号。repo
repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'
如果您有兴趣,请进行说明。
从 shell 运行命令时,该命令作为参数接收的是以 null 结尾的字符串数组。这些字符串绝对可以包含任何非 null 字符。
但是,当 shell 从命令行构建该字符串数组时,它会专门解释某些字符;这是为了让命令更容易(实际上,可能)键入。例如,空格通常表示数组中字符串之间的边界;因此,shell 中的字符串通常称为“单词”。但是,一个 shell 单词中可能仍然有空格;你只需要某种方式来告诉 shell 这就是你想要的。
您可以在任何字符(包括空格或其他反斜杠)前面使用反斜杠来告诉 shell 按字面意思处理该字符。但是,虽然你可以做这样的事情:
reply=\”That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier
...它可能会变得令人厌烦。因此,shell 提供了一个替代方案:引号。这些主要有两个品种。
双引号称为“分组引号”。它们可以防止扩展通配符和别名,但大多数情况下,它们用于在单词中包含空格。其他事情,如参数和命令扩展(由 a 发出信号的那种事情)仍然会发生。当然,如果你想在双引号内有一个文字双引号,你必须反斜杠它:$
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'
需要注意的重要一点是,在所有这些示例中,存储在变量中的单个字符串完全相同。同样,在 shell 完成对命令行的解析后,运行的命令无法准确告知每个参数字符串的实际类型化方式,或者即使它被类型化,也无法以某种方式以编程方式创建。reply
评论
$'string'
编辑:(根据有问题的评论:)
从那时起,我一直在研究这个问题。我很幸运,我有回购。不过,我不清楚你是否需要强行将命令括在单引号之间。我研究了 repo 语法,我认为您不需要。您可以在命令周围使用双引号,然后在内部使用所需的任何单引号和双引号,前提是您转义了双引号。
以下是对我有用的东西 -
QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
评论
QUOTE
这对你有用吗?
eval repo forall -c '....$variable'
评论
eval
在通过解析器运行该字符串之前,将其所有参数混合到一个字符串中;在这种情况下,它引起的问题远远多于它解决的问题。
变量可以包含单引号。
myvar=\'....$variable\'
repo forall -c $myvar
评论
"$myvar"
只需使用 printf
而不是
repo forall -c '....$variable'
使用 printf 将变量 token 替换为扩展变量。
例如:
template='.... %s'
repo forall -c $(printf "${template}" "${variable}")
评论
printf
我想知道为什么我永远无法从 ssh 会话中打印我的 awk 声明,所以我找到了这个论坛。这里没有任何东西直接帮助我,但如果有人遇到类似于下面的问题,请给我投赞成票。似乎任何形式的单引号或双引号都无济于事,但后来我没有尝试所有方法。
check_var="df -h / | awk 'FNR==2{print $3}'"
getckvar=$(ssh user@host "$check_var")
echo $getckvar
你会得到什么?一无所有。
修复:在打印功能中转义。\$3
评论
repo forall -c ' ...before... '"$variable"' ...after...'
bash
吃单引号。要么不在 中,要么单引号不是命令的一部分。bash
repo