在 Bash 中的命令中扩展单引号内的变量
Posted
技术标签:
【中文标题】在 Bash 中的命令中扩展单引号内的变量【英文标题】:Expansion of variables inside single quotes in a command in Bash 【发布时间】:2022-01-09 09:48:29 【问题描述】:我想从一个 bash 脚本 运行一个命令,该脚本有单引号和单引号内的一些其他命令和一个变量。
例如repo forall -c '....$variable'
在这种格式中,$
被转义,变量不展开。
我尝试了以下变体,但都被拒绝了:
repo forall -c '...."$variable" '
repo forall -c " '....$variable' "
" repo forall -c '....$variable' "
repo forall -c "'" ....$variable "'"
如果我用值代替变量,则命令执行得很好。
请告诉我哪里出错了。
【问题讨论】:
repo forall -c ' ...before... '"$variable"' ...after...'
@n.m.单引号是 repo 命令的一部分。我认为这行不通
bash
吃单引号。要么您不在bash
中,要么单引号不是repo
命令的一部分。
不确定我明白了,但如果需要单引号,不会回购 forall -c "' ...before... "$variable" ...after.. .'”工作?
它是一个在 bash shell 中运行的 bash 脚本,repo forall 命令将参数放在单引号内。如果我替换实际值,命令运行正常。
【参考方案1】:
在单引号内,所有内容都按字面意思保留,无一例外。
这意味着你必须关闭引号,插入一些东西,然后重新输入。
'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
的赋值中使用了单个刻度,这意味着它是按字面意思表示的,没有变量扩展或任何其他形式的解释。
【讨论】:
@Jo.SO 那行不通。因为 repo 命令将只接受参数,直到第一个引号被关闭。之后的任何内容都将被 repo 忽略并产生另一个错误。 @Rachit - 外壳不能那样工作。"some"thing' like'this
对 shell 来说就是一个词。就解析而言,引号不会终止任何内容,并且 by shell 调用的命令无法说明引用的方式。
第二句话(“你甚至不能转义单引号”)使单引号成为 Shell 的黑洞。
@Evert:不知道怎么说更好。我已经删除了这句话。
@DanielKaplan 简单的规则是总是引用你的变量,否则你会想出鼻恶魔,比如在空格上分割甚至是全局扩展。总是引用你的变量。【参考方案2】:
repo
命令不关心它得到什么样的引号。如果需要参数扩展,请使用双引号。如果这意味着你最终不得不反斜杠很多东西,对大部分内容使用单引号,然后将它们分开并在需要扩展的部分使用双引号。
repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'
说明如下,如果您有兴趣。
当您从 shell 运行命令时,该命令作为参数接收的是一个以 null 结尾的字符串数组。这些字符串可能绝对包含 any 非空字符。
但是当 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
变量中的单个字符串完全相同。同样,在 shell 完成对命令行的解析之后,正在运行的命令无法准确说明每个参数字符串是如何实际键入的——甚至 if 它是键入的,而不是创建的以某种方式编程。
【讨论】:
是的。我现在知道了。它完美地工作。非常感谢您的帮助! 这个回复太棒了。$'string'
格式是否符合 POSIX?另外,它有名字吗?
@Wildcard $'string'
是一个非 POSIX 扩展,我看到它被称为“ANSI 字符串”;我已将这些事实纳入答案。此类字符串适用于大多数现代 Bourne 兼容的 shell:bash、dash、ksh(AT&T 和 PD)和 zsh 都支持它们。
链接回 - What characters are required to be escaped in command line arguments? 在 Unix 和 Linux 堆栈交换上。【参考方案3】:
以下是对我有用的方法-
QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
【讨论】:
我希望 TBL_HDFS_DIR_PATH 不是用户提供的输入顺便说一句,这将允许一个很好的 sql 注入... 将QUOTE
放在一个单独的变量中是完全多余的;双引号内的单引号只是一个常规字符。 (另外,不要对私有变量使用大写。)【参考方案4】:
编辑:(根据有问题的 cmets:)
从那以后我一直在研究这个问题。我很幸运,我有回购协议。我仍然不清楚您是否需要强制将命令括在单引号之间。我查看了 repo 语法,我认为您不需要。你可以在你的命令周围使用双引号,然后使用你需要的任何单引号和双引号,只要你转义双引号。
【讨论】:
感谢凯文的帮助。【参考方案5】:只需使用 printf
而不是
repo forall -c '....$variable'
使用 printf 将变量标记替换为扩展变量。
例如:
template='.... %s'
repo forall -c $(printf "$template" "$variable")
【讨论】:
这个坏了;printf
在这里并没有真正给你买任何东西,并且未能引用命令替换会导致新的引用问题。【参考方案6】:
变量可以包含单引号。
myvar=\'....$variable\'
repo forall -c $myvar
【讨论】:
他们可以,但这不是正确的做法——您仍应将"$myvar"
放在双引号中。【参考方案7】:
这对你有用吗?
eval repo forall -c '....$variable'
【讨论】:
适合你吗?这个问题已经有五年的历史了,已经有了一些答案,甚至是一个被接受的答案......以上是关于在 Bash 中的命令中扩展单引号内的变量的主要内容,如果未能解决你的问题,请参考以下文章