将输出分配给变量时是否需要引用命令替换?

ter*_*don 6 shell quoting command-substitution

即使将其输出分配给变量,我也倾向于引用如下所示的命令替换:

var="$(command)"
Run Code Online (Sandbox Code Playgroud)

但这真的需要吗?什么时候断?此处接受的答案声称:

DIRNAME = “$(目录名称$ FILE)”不会做你想做的,如果$文件包含空格或的通配符[?*。

该链接指向 Gray Cat Wiki 关于引用的精彩页面,但该页面没有特别提及引用命令替换。虽然显然需要引用变量,但引用命令替换本身似乎并不需要。

然而,同一篇文章的结尾是:

DIRNAME="$(dirname "$FILE")" 是推荐的方式。您可以用命令和空格替换 DIRNAME= 而不更改任何其他内容,并且 dirname 接收正确的字符串。

这也是我一直以来的想法,并且经常在这里修改没有引用它的帖子。但是,链接到上面的维基页面也声称:

有几种情况可以安全地省略双引号:

在简单作业的右侧。你可以写 foo=$bar 不带引号。这是符合 POSIX 的。

[. . . ]

虽然var=$(command)不是真正的“简单”任务,但我仍然无法找到实际需要引号的情况:

$ var=$(echo "foo bar baz")  ## whitespace works
$  echo "$var"
foo bar baz

$ var=$(printf "foo\nbar * baz") ## so do globbing characters
$ echo "$var"
foo
bar * baz

$ var1="foo\nbar * baz"
$ var=$(printf "$var1")  ## printing a variable doesn't make any difference
$ echo "$var" 
foo
bar * baz

$ var=$(printf '%s\n' "$var1")
$ echo "$var"
foo\nbar * baz

$ var=$(printf -- '-e %s\n' "$var1") ## strings starting with - also work
$ echo "$var"
-e foo\nbar * baz
Run Code Online (Sandbox Code Playgroud)

当然,如果命令替换直接用于诸如 之类的东西,引号是绝对必要的command1 "$(command2)",但在分配给变量时似乎并非如此。

那么,我错过了什么?是否需要引用?在将返回值分配给变量时引用命令替换会保护您免受什么极端情况?或者如果它是变量赋值操作的右侧,不引用命令替换总是可以的?

rud*_*ier 7

您不需要引用赋值右侧的表达式。

令您恼火的是,另一个答案仍然建议引用。但这只是关于代码维护。

考虑以下正确的例子:

DIRNAME=$(dirname "$FILE")
echo "debug: dirname is $DIRNAME"
ls "$DIRNAME"
Run Code Online (Sandbox Code Playgroud)

现在,使用此脚本一段时间后,您可能会认为可以删除调试消息。因此,使用编辑器您将删除回显线。然后您会注意到,您甚至不再需要变量 DIRNAME,只需移动命令ls来替换分配的左侧站点即可。现在您可能忘记添加所需的引号,最终会得到这个损坏的脚本

ls $(dirname "$FILE")
Run Code Online (Sandbox Code Playgroud)

如果第一作者是 shell 专家而第二编辑是新手,则出现此类错误的可能性会更高。

当然,这种避免使用可移植 shell 功能的建议是否真的有用还值得商榷。就我个人而言,我主要按照他的建议去做。我也为更“简单”的作业执行此操作,例如:(var="${foo}"也包括多余的花括号)。


ilk*_*chu 5

作为参考,Bash 的手册对此很清楚

可以通过以下形式的语句将变量赋值给

name=[value]

如果未给出值,则为该变量分配空字符串。所有值都会经历波形符扩展、参数和变量扩展、命令替换、算术扩展和引号删除(详细信息如下)。[...]不执行分词,但“$@”除外,如下所述。不执行文件名扩展。

没有分词,没有文件名扩展,因此不需要引号。


至于 POSIX,第2.9.1 节简单命令

2、非变量赋值或重定向的词应展开。如果扩展后仍保留任何字段 如果扩展后仍保留任何字段,则第一个字段应被视为命令名称,其余字段是命令的参数。
[...]
4. 每个变量赋值都应在赋值之前进行扩展,以进行波形符扩展、参数扩展、命令替换、算术扩展和引号删除。

我不确定这是否应该被解释为字段分割仅发生在步骤 2 中完成的扩展中?步骤 4 没有提到字段拆分,尽管有关字段拆分的部分也没有提到变量赋值作为生成多个字段的例外。