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

Rac*_*hit 344 variables bash shell quotes

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

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

在此格式中,$进行转义并且不扩展变量.

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

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

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

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

repo forall -c "'" ....$variable "'"
Run Code Online (Sandbox Code Playgroud)

如果我用值代替变量来执行命令就好了.

请告诉我哪里出错了.

Jo *_* So 554

在单引号内,一切都是字面上保留,毫无例外.

这意味着你必须关闭引号,插入一些东西,然后再次重新输入.

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'
Run Code Online (Sandbox Code Playgroud)

您可以验证,上面的每一行都是shell的单个单词.字符串连接简单地通过并置来完成.引号(单引号或双引号,取决于具体情况)用于禁用各种特殊字符的解释,如空格$,;... ...有关引用的好教程请参阅Mark Reed的答案.同样相关:哪些角色需要在bash中转义?

不要连接shell解释的字符串

您绝对应该通过连接变量来避免构建shell命令.这是一个类似于SQL片段连接的坏主意(SQL注入!).

通常可以在命令中包含占位符,并将命令与变量一起提供,以便被调用者可以从调用参数列表中接收它们.

例如,以下内容非常不安全.不要这样做

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"
Run Code Online (Sandbox Code Playgroud)

如果内容$myvar不受信任,这里有一个漏洞利用:

myvar='foo"; echo "you were hacked'
Run Code Online (Sandbox Code Playgroud)

而不是上面的调用,使用位置参数.以下调用更好 - 它不可利用:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"
Run Code Online (Sandbox Code Playgroud)

注意在赋值中使用单个刻度script,这意味着它是字面上的,没有可变扩展或任何其他形式的解释.

  • @Rachit - shell不能那样工作."某些"事物'就像'这个'对壳来说都是一个字.就解析而言,引号不会终止任何内容,并且没有办法让一个名为*by*shell的命令告诉引用的内容. (8认同)
  • 第二句话("你甚至无法逃脱单引号")使单引号成为壳牌的黑洞. (3认同)
  • @DanielKaplan 简单的规则是“总是引用你的变量”,否则你会召唤出鼻恶魔,比如在空格上分割,甚至全局扩展。始终引用您的变量。 (3认同)

Mar*_*eed 84

repo命令无关心它得到什么样的引号.如果需要参数扩展,请使用双引号.如果这意味着你不得不反复使用大量的东西,那么对大多数东西使用单引号,然后突破它们并进入双打,以便你需要进行扩展.

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'
Run Code Online (Sandbox Code Playgroud)

如果您有兴趣,请说明如下.

从shell运行命令时,该命令作为参数接收的是一个以null结尾的字符串数组.这些字符串可以包含绝对任何非空字符.

但是当shell从命令行构建那个字符串数组时,它会特别解释一些字符; 这旨在使命令更容易(实际上,可能)键入.例如,空格通常表示数组中字符串之间的边界; 因此,个别论点有时被称为"单词".但是,一个论点可能会有空格; 你需要一些方法来告诉shell你想要什么.

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

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

......它可能会让人厌倦.因此shell提供了另一种选择:引号.这有两个主要品种.

双引号称为"分组引号".它们可以防止通配符和别名被扩展,但主要是它们用于包含单词中的空格.其他一些事情,比如参数和命令扩展(由a发出的各种事情$)仍然会发生.当然,如果你想在双引号内使用双字引号,你必须反斜杠:

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换行符,\t制表符和\xHH字符十六进制代码HH).否则,它们表现为单引号字符串:不进行参数或命令替换:

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'
Run Code Online (Sandbox Code Playgroud)

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

  • 这个回复太棒了. (6认同)
  • 是.我现在知道了.它完美地运作.非常感谢你的帮助! (2认同)
  • @Wildcard` $'string'`是一个非POSIX扩展,我看到它被称为"ANSI字符串"; 我已将这些事实纳入答案.这些字符串适用于大多数现代Bourne兼容的shell:bash,dash,ksh(AT&T和PD),zsh都支持它们. (2认同)

小智 6

以下是对我有用的 -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
Run Code Online (Sandbox Code Playgroud)

  • 我希望 TBL_HDFS_DIR_PATH 不是用户提供的输入顺便说一句,这将允许一个很好的 SQL 注入... (2认同)
  • 将“QUOTE”放在单独的变量中是完全多余的;双引号内的单引号只是常规字符。(另外,不要对私有变量使用大写。) (2认同)