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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13799789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:05:17  来源:igfitidea点击:

Expansion of variables inside single quotes in a command in Bash

bashshellvariablesquotes

提问by Rachit

I want to run a command from a bash scriptwhich has single quotes and some other commands inside the single quotes and a variable.

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

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

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

In this format, $is escaped and the variable is not expanded.

在这种格式中,$被转义并且变量不被扩展。

I tried the following variations but they were rejected:

我尝试了以下变体,但被拒绝了:

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

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

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

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

If I substitute the value in place of the variable the command is executed just fine.

如果我用值代替变量,则命令执行得很好。

Please tell me where am I going wrong.

请告诉我我哪里出错了。

回答by Jo So

Inside single quotes everything is preserved literally, without exception.

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

That means you have to close the quotes, insert something, and then re-enter again.

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

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

Word concatenation is simply done by juxtaposition. As you can verify, each of the above lines is a single word to the shell. Quotes (single or double quotes, depending on the situation) don't isolate words. They are only used to disable interpretation of various special characters, like whitespace, $, ;... For a good tutorial on quoting see Mark Reed's answer. Also relevant: Which characters need to be escaped in bash?

单词连接只是通过并列完成的。正如您可以验证的那样,上面的每一行都是 shell 的一个单词。引号(单引号或双引号,视情况而定)不会隔离单词。它们仅用于禁用对各种特殊字符的解释,如空格$、, ;...有关引用的好教程,请参阅 Mark Reed 的回答。同样相关:哪些字符需要在 bash 中转义?

Do not concatenate strings interpreted by a shell

不要连接由 shell 解释的字符串

You should absolutely avoid building shell commands by concatenating variables. This is a bad idea similar to concatenation of SQL fragments (SQL injection!).

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

Usually it is possible to have placeholders in the command, and to supply the command together with variables so that the callee can receive them from the invocation arguments list.

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

For example, the following is very unsafe. DON'T DO THIS

例如,以下是非常不安全的。不要这样做

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

If the contents of $myvaris untrusted, here is an exploit:

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

myvar='foo"; echo "you were hacked'

Instead of the above invocation, use positional arguments. The following invocation is better -- it's not exploitable:

使用位置参数代替上述调用。以下调用更好——它不可利用:

script='echo "arg 1 is: "'
/bin/sh -c "$script" -- "$myvar"

Note the use of single ticks in the assignment to script, which means that it's taken literally, without variable expansion or any other form of interpretation.

请注意在赋值中使用单个刻度线 to script,这意味着它是按字面意思理解的,没有变量扩展或任何其他形式的解释。

回答by Mark Reed

The repocommand can't care what kind of quotes it gets. If you need parameter expansion, use double quotes. If that means you wind up having to backslash a lot of stuff, use single quotes for most of it, and then break out of them and go into doubles for the part where you need the expansion to happen.

repo命令不关心它得到什么样的引号。如果需要参数扩展,请使用双引号。如果这意味着您最终不得不反斜杠很多东西,那么对大部分内容使用单引号,然后将它们分开并在需要扩展的部分进入双引号。

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

Explanation follows, if you're interested.

解释如下,如果你有兴趣。

When you run a command from the shell, what that command receives as arguments is an array of null-terminated strings. Those strings may contain absolutely anynon-null character.

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

But when the shell is building that array of strings from a command line, it interprets some characters specially; this is designed to make commands easier (indeed, possible) to type. For instance, spaces normally indicate the boundary between strings in the array; for that reason, the individual arguments are sometimes called "words". But an argument may nonetheless have spaces in it; you just need some way to tell the shell that's what you want.

但是当 shell 从命令行构建字符串数组时,它会特别解释一些字符;这旨在使命令更容易(实际上,可能)输入。例如,空格通常表示数组中字符串之间的边界;因此,个别论点有时被称为“词”。但是一个论点可能仍然有空格;您只需要某种方式来告诉外壳这就是您想要的。

You can use a backslash in front of any character (including space, or another backslash) to tell the shell to treat that character literally. But while you can do something like this:

您可以在任何字符(包括空格或另一个反斜杠)前使用反斜杠来告诉外壳程序按字面意思处理该字符。但是,虽然您可以执行以下操作:

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

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

...it can get tiresome. So the shell offers an alternative: quotation marks. These come in two main varieties.

......它可能会让人厌烦。所以 shell 提供了一个替代方案:引号。这些有两个主要品种。

Double-quotation marks are called "grouping quotes". They prevent wildcards and aliases from being expanded, but mostly they're for including spaces in a word. Other things like parameter and command expansion (the sorts of thing signaled by a $) still happen. And of course if you want a literal double-quote inside double-quotes, you have to backslash it:

双引号称为“分组引号”。它们防止扩展通配符和别名,但主要用于在单词中包含空格。诸如参数和命令扩展之类的其他事情(由 a 表示的那种事情$)仍然会发生。当然,如果你想要双引号内的文字双引号,你必须反斜线它:

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

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

Single-quotation marks are more draconian. Everything between them is taken completely literally, including backslashes. There is absolutely no way to get a literal single quote inside single quotes.

单引号更加严厉。它们之间的所有内容都完全按字面意思理解,包括反斜杠。绝对没有办法在单引号中获得文字单引号。

Fortunately, quotation marks in the shell are not word delimiters; by themselves, they don't terminate a word. You can go in and out of quotes, including between different types of quotes, within the same word to get the desired result:

幸运的是,shell中的引号不是单词分隔符;就其本身而言,它们不会终止一个词。您可以在同一个单词内进入和退出引号,包括在不同类型的引号之间,以获得所需的结果:

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

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

So that's easier - a lot fewer backslashes, although the close-single-quote, backslashed-literal-single-quote, open-single-quote sequence takes some getting used to.

所以这更容易 - 更少的反斜杠,尽管关闭单引号,反斜杠文字单引号,开单引号序列需要一些时间来适应。

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 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 \nfor a newline, \tfor tab, and \xHHfor the character with hexadecimal code HH). Otherwise, however, they behave as single-quoted strings: no parameter or command substitution takes place:

现代 shell 添加了另一种 POSIX 标准未指定的引用样式,其中前导单引号以美元符号为前缀。如此引用的字符串遵循与 ANSI C 编程语言中的字符串文字类似的约定,因此有时称为“ANSI 字符串”和$'...'对“ANSI 引号”。在此类字符串中,上述有关从字面上采用反斜杠的建议不再适用。相反,它们再次变得特殊——您不仅可以通过在其前面添加反斜杠来包含文字单引号或反斜杠,而且 shell 还扩展了 ANSI C 字符转义(例如\n换行符、\t制表符和\xHH带有十六进制代码HH)。然而,否则,它们表现为单引号字符串:不发生参数或命令替换:

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

The important thing to note is that the single string received as the argument to the echocommand is exactly the samein all of these examples. After the shell is done parsing a command line, there is no way for the command being run to tell what was quoted how. Even if it wanted to.

需要注意的重要一点是,在所有这些示例中,作为echo命令参数接收的单个字符串完全相同。在 shell 完成对命令行的解析后,正在运行的命令无法判断引用的内容是如何引用的。即使它想。

回答by Manmohan

Below is what worked for me -

以下是对我有用的内容-

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

回答by ecv

EDIT:(As per the comments in question:)

编辑:(根据相关评论:)

I've been looking into this since then. I was lucky enough that I had repo laying around. Still it's not clear to me whether you need to enclose your commands between single quotes by force. I looked into the repo syntax and I don't think you need to. You could used double quotes around your command, and then use whatever single and double quotes you need inside provided you escape double ones.

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

回答by AndrewD

just use printf

只需使用 printf

instead of

代替

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

use printf to replace the variable token with the expanded variable.

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

For example:

例如:

template='.... %s'

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

回答by user3734617

Variables can contain single quotes.

变量可以包含单引号。

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

repo forall -c $myvar

回答by drgnfr

Does this work for you?

这对你有用吗?

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