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

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

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

我时常遇到这种情况,并认为这是要走的路:

$(comm "$(arg)")

并认为我的观点得到了经验的有力支持。但我不再那么确定了。外壳检查也拿不定主意。两者都是:

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

和:

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

背后的逻辑是什么?

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

答案1

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

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

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

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

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


例子:

$ file="./space here/foo"

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

$ printf "<%s>\n" $(dirname $file)
<.>
<here>

b.里面的引号,dirnameprocesses ./space here/foo,giving ./space here,被分成两部分:

$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>

C。外部引号,dirname处理./spacehere/foo,在不同的行上输出,但是现在这两行形成一个参数:

$ printf "<%s>\n" "$(dirname $file)"
<.
here>

d.引用内部和外部,这给出了正确答案:

$ printf "<%s>\n" "$(dirname "$file")"
<./space here>

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

请注意,对于dirname(可能还有其他),您还需要添加--, 以防止文件名被视为选项,以防它恰好以破折号开头,因此请使用"$(dirname -- "$file")".

相关内容