我想从一个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"

请注意在脚本的赋值中使用了单勾,这意味着它是字面上的,没有变量展开或任何其他形式的解释。


repo命令不会关心它得到什么样的引号。如果需要展开参数,请使用双引号。如果这意味着你最终不得不反斜杠很多东西,大部分使用单引号,然后在需要展开的部分使用双引号。

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

如果你感兴趣,下面是解释。

当您从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'

这样就更简单了——反斜杠少了很多,尽管闭单引号、反斜杠-字面量-单引号、开单引号序列需要一些时间来适应。

Modern shells have added another quoting style not specified by the POSIX standard, in which the leading single quotation mark is prefixed with a dollar sign. Strings so quoted follow similar conventions to string literals in the ANSI standard version of the C programming language, and are therefore sometimes called "ANSI strings" and the $'...' pair "ANSI quotes". Within such strings, the above advice about backslashes being taken literally no longer applies. Instead, they become special again - not only can you include a literal single quotation mark or backslash by prepending a backslash to it, but the shell also expands the ANSI C character escapes (like \n for a newline, \t for tab, and \xHH for the character with hexadecimal code HH). Otherwise, however, they behave as single-quoted strings: no parameter or command substitution takes place:

reply=$'"That\'ll be $4.96, please," said the cashier'

需要注意的重要一点是,在所有这些示例中,存储在应答变量中的单个字符串是完全相同的。类似地,在shell解析完命令行之后,运行的命令无法确切地告诉每个参数字符串实际上是如何键入的——甚至无法知道它是否键入了,而不是以某种编程方式创建的。


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

从那以后我一直在调查这件事。我很幸运,手头有回购。我仍然不清楚你是否需要把你的命令用单引号括起来。我研究了回购语法,我认为你不需要。您可以在命令周围使用双引号,然后在命令内部使用任何您需要的单引号和双引号,前提是要转义双引号。


下面是对我有效的方法

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

这对你有用吗?

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

变量可以包含单引号。

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

repo forall -c $myvar

只需使用printf

而不是

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

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

例如:

template='.... %s'

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

我想知道为什么我永远不能让我的awk语句从ssh会话打印,所以我找到了这个论坛。这里没有什么直接帮助我,但如果有人有类似的问题,然后给我一个投票。似乎任何单引号或双引号都没有帮助,但我并没有尝试所有的方法。

check_var="df -h / | awk 'FNR==2{print $3}'"
getckvar=$(ssh user@host "$check_var")
echo $getckvar

你得到了什么?一无所有。

修复:逃脱\$3在您的打印功能。