我想从一个 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...'
bash
吃单引号。要么您不在 bash
中,要么单引号不是 repo
命令的一部分。
在单引号内,所有内容都按字面意思保留,无一例外。
这意味着您必须关闭引号,插入一些内容,然后再次重新输入。
'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
的赋值中使用了单个刻度,这意味着它是按字面意思进行的,没有变量扩展或任何其他形式的解释。
repo
命令不关心它得到什么样的引号。如果需要参数扩展,请使用双引号。如果这意味着你最终不得不反斜杠很多东西,对大部分内容使用单引号,然后将它们分开并在需要扩展的部分使用双引号。
repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'
解释如下,如果你有兴趣。
当您从 shell 运行命令时,该命令作为参数接收的是一个以 null 结尾的字符串数组。这些字符串可能绝对包含任何非空字符。
但是当 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?另外,它有名字吗?
$'string'
是一个非 POSIX 扩展,我将其称为“ANSI 字符串”;我已将这些事实纳入答案。这样的字符串可以在大多数现代 Bourne 兼容的 shell 中工作:bash、dash、ksh(AT&T 和 PD)和 zsh 都支持它们。
以下是对我有用的 -
QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
QUOTE
放在单独的变量中是完全多余的;双引号内的单引号只是一个常规字符。 (另外,不要对私有变量使用大写。)
编辑:(根据有问题的评论:)
从那以后我一直在研究这个。我很幸运,我有回购协议。我仍然不清楚您是否需要强制将命令括在单引号之间。我查看了 repo 语法,我认为您不需要。你可以在你的命令周围使用双引号,然后使用你需要的任何单引号和双引号,只要你转义双引号。
只需使用 printf
代替
repo forall -c '....$variable'
使用 printf 将变量标记替换为扩展变量。
例如:
template='.... %s'
repo forall -c $(printf "${template}" "${variable}")
printf
在这里并没有真正为您买任何东西,并且未能引用命令替换会导致新的引用问题。
变量可以包含单引号。
myvar=\'....$variable\'
repo forall -c $myvar
"$myvar"
放在双引号中。
这对你有用吗?
eval repo forall -c '....$variable'
"some"thing' like'this
对 shell 来说是一个词。就解析而言,引号不会终止任何内容,并且调用 by shell 的命令无法说明引用的方式。