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


17

现在是解决困扰我多年的难题的时候了...

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

$(comm "$(arg)")

并且认为我的观点得到了经验的大力支持。但是我不太确定了。Shellcheck也无法下定决心。两者都是:

"$(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
引用所有替换项。
伊格纳西奥·巴斯克斯

3
就像这样:"$(dirname "$0")"/stop.bash?似乎有效...这是什么故事?
Tomasz

1
bash非常聪明,可以知道上下文何时更改。
伊格纳西奥·巴斯克斯

1
像这样想shell_level_1 $(shell_level_2) shell_level_1::当您$(....)进入shell时,您将进入shell的“子级别”,但您可以像在主级别上一样在其中进行写(即,您可以直接编写"而不是\",等等)。例如: touch "/tmp/a file" ; echo "its size is: $(find "/tmp/a file" -ls | awk '{print $5}) ..." : if you used backticks you'd have to 找到\“ / tmp / a文件\”`和print \$5。随着$(...)无需:外壳适应新的水平,你可以直接写就好像你的解释,现在是在那个水平了。
奥利维尔·杜拉克

“ Shellcheck也不能下定决心。” -两次运行有两个不同的消息,并且指向不同的位置。它想要两个。
伊兹卡塔

Answers:


45

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

如果没有引号,则会在的参数中分隔带空格的路径,并且该路径somecmd将定位到错误的文件。因此,您需要在内部加引号。

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

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

使用该$(command)格式,在开括号后面到匹配的闭括号后面的所有字符构成命令。可以将任何有效的Shell脚本用于命令,但仅由重定向组成的脚本会产生未指定的结果。


例:

$ file="./space here/foo"

一种。无引号,同时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")"


16
注意:通常,引号不会嵌套在bash中;但是在这种情况下,$( )会创建一个新的上下文,因此其内部的引号与外部的引号无关。通常,您都希望在两种情况下都使用引号。
戈登·戴维森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.