ChatGPT解决这个技术问题 Extra ChatGPT

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

我想从一个 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...'
@nm 单引号是 repo 命令的一部分。我认为这行不通
bash 吃单引号。要么您不在 bash 中,要么单引号不是 repo 命令的一部分。
不确定我明白你,但如果需要单引号,repo forall -c "' ...before..."$variable" ...after...'" 不会工作吗?
它是一个在 bash shell 中运行的 bash 脚本,repo forall 命令将参数放在单引号内。如果我替换实际值,则命令运行正常。

J
Jo So

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

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

'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 简单的规则总是引用你的变量,否则你会变出鼻恶魔,比如在空格上分割甚至是全局扩展。总是引用你的变量。
M
Mark Reed

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?另外,它有名字吗?
@Wildcard $'string' 是一个非 POSIX 扩展,我将其称为“ANSI 字符串”;我已将这些事实纳入答案。这样的字符串可以在大多数现代 Bourne 兼容的 shell 中工作:bash、dash、ksh(AT&T 和 PD)和 zsh 都支持它们。
回链接 - Unix 上的 What characters are required to be escaped in command line arguments? & Linux 堆栈交换。
m
melpomene

以下是对我有用的 -

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

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

编辑:(根据有问题的评论:)

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


感谢凯文的帮助。
A
AndrewD

只需使用 printf

代替

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

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

例如:

template='.... %s'

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

这坏了; printf 在这里并没有真正为您买任何东西,并且未能引用命令替换会导致新的引用问题。
R
Robert Columbia

变量可以包含单引号。

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

repo forall -c $myvar

他们可以,但这不是正确的做法 - 您仍应将 "$myvar" 放在双引号中。
d
drgnfr

这对你有用吗?

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

适合你吗?这个问题已经有五年的历史了,已经有了一些答案,甚至是一个被接受的答案......