引用 $(command $arg) 的正确方法是什么?

23 bash quoting command-substitution

是时候解决这个困扰我多年的难题了......

我不时遇到这个问题,并认为这是要走的路:

$(comm "$(arg)")
Run Code Online (Sandbox Code Playgroud)

并认为我的观点得到了经验的强烈支持。但我不再那么确定了。Shellcheck也拿不定主意。两者都是:

"$(dirname $0)"/stop.bash
           ^-- SC2086: Double quote to prevent globbing and word splitting.
Run Code Online (Sandbox Code Playgroud)

和:

$(dirname "$0")/stop.bash
^-- SC2046: Quote this to prevent word splitting.
Run Code Online (Sandbox Code Playgroud)

背后的逻辑是什么?

(这是 Shellcheck 0.4.4 版,顺便说一句。)

ilk*_*chu 50

您需要使用"$(somecmd "$file")".

如果没有引号,带有空格的路径将在参数中拆分为somecmd,并且它将定位到错误的文件。所以你需要在里面引用。

输出中的任何空格somecmd也会导致拆分,因此您需要在整个命令替换的外部加上引号。

命令替换内的引号对其外部的引号没有影响。Bash 自己的参考手册对此不太清楚,但BashGuide 明确提到了它POSIX 中文本也需要它,因为内部允许“任何有效的 shell 脚本”$(...)

对于$(command)表单,左括号后面到匹配右括号的所有字符构成命令。任何有效的 shell 脚本都可用于命令,但仅由重定向组成的脚本除外,该脚本会产生未指定的结果。


例子:

$ file="./space here/foo"
Run Code Online (Sandbox Code Playgroud)

一种。没有引号,同时dirname处理./spacehere/foo

$ printf "<%s>\n" $(dirname $file)
<.>
<here>
Run Code Online (Sandbox Code Playgroud)

湾 里面的行情,dirname过程./space here/foo,给予./space here,分为两部分:

$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>
Run Code Online (Sandbox Code Playgroud)

C。Quotes outside,dirname处理./spaceand here/foo, 在单独的行上输出,但现在这两行形成一个参数

$ printf "<%s>\n" "$(dirname $file)"
<.
here>
Run Code Online (Sandbox Code Playgroud)

d. 内外引述,这给出了正确的答案:

$ printf "<%s>\n" "$(dirname "$file")"
<./space here>
Run Code Online (Sandbox Code Playgroud)

(如果dirname只处理第一个参数,那可能会更简单,但这不会显示情况 a 和 c 之间的区别。)

请注意,与dirname(可能还有其他人)一起,您还需要添加--, 以防止将文件名作为选项,以防它碰巧以破折号开头,因此请使用"$(dirname -- "$file")".

  • 注意:通常,引号不会嵌套在 bash 中;但是在这种情况下,`$()` 创建了一个新的上下文,所以它里面的引号独立于它外面的引号。并且您通常希望在两种情况下都使用引号。 (18认同)