在Bash环境中,我使用包含空格的变量,并在命令替换中使用这些变量。不幸的是,我找不到关于SE的答案。

引用变量的正确方法是什么?如果将它们嵌套,该怎么办?

 DIRNAME=$(dirname "$FILE")
 


还是在外面引用

 DIRNAME="$(dirname $FILE)"
 


还是两者?

 DIRNAME="$(dirname "$FILE")"
 


还是使用反引号?

 DIRNAME=`dirname "$FILE"`
 


什么是正确的方法?以及如何轻松检查引号设置是否正确?

评论

另请参见-unix.stackexchange.com/questions/97560/…

另请参见什么时候需要双引号?和$ VAR与$ {VAR}并引用或不引用

这是一个很好的问题,但是考虑到所有与嵌入式毛坯有关的问题,为什么您会故意使用它们而使自己的生活变得艰难?
@Joe,带空格的空白是指文件名中的空格吗?我个人并不经常使用它们,但是我正在与其他人的目录和文件一起工作,我不确定它们是否包含空格。此外,我认为最好一次完成它,这样我以后就不必担心。

是。我们要和那些“其他人”做什么?

#1 楼


按从大到小的顺序排列:




如果DIRNAME="$(dirname $FILE)"包含空格或glob字符$FILE,则\[?*将无法执行您想要的操作。 > DIRNAME=`dirname "$FILE"`在技术上是正确的,但不建议在命令扩展中使用反引号,因为嵌套它们时会增加额外的复杂性。如果在任何其他上下文中使用命令替换,例如DIRNAME=$(dirname "$FILE")export DIRNAME=$(dirname "$FILE"),则如果扩展结果包含空格或glob字符,则缺少引号会引起麻烦。推荐使用du $(dirname "$FILE")。您可以使用命令和空格替换DIRNAME="$(dirname "$FILE")"而不更改任何其他内容,并且DIRNAME=会接收正确的字符串。

进一步改进: >如果dirname以短划线开头,则DIRNAME="$(dirname -- "$FILE")"可以工作。 Sheesh $FILE,为什么要与众不同?

您可以随意嵌套命令扩展。使用DIRNAME="$(dirname -- "$FILE"; printf x)" && DIRNAME="${DIRNAME%?x}",您总是可以创建一个新的引用上下文,因此您可以执行以下操作:

 $FILE 


您不想尝试反引号。

评论


明确回答我的问题。当我嵌套这些变量时,是否可以像现在一样继续引用?

–库辛可卡因
2014年3月6日15:07

是否有参考/资源详细说明了引号内命令替换内引号的行为?

– AmadeusDrZaius
15年3月4日在18:43

@AmadeusDrZaius“使用$(),您总是可以创建一个新的引用上下文”,因此就像在外部引号之外一样。据我所知,仅此而已。

–l0b0
15年3月4日在23:05

@ l0b0谢谢,是的,我发现您的解释很清楚。我只是想知道它是否也在某个地方的手册中。我确实在羊毛边缘找到了它(尽管是非正式的)。我想如果您仔细阅读有关替换顺序的信息,则可以得出这个事实。

– AmadeusDrZaius
2015年3月4日23:13



因此嵌套的引号是可以接受的,但是它们使我们望而却步,因为大多数语法着色方案都无法检测到特殊情况。整齐。

–卢克·戴维斯(Luke Davis)
17年8月4日在16:40

#2 楼

您始终可以使用printf来显示变量引用的效果。

var1上的单词拆分完成了: :

$ var1="hello     world"
$ printf '[%s]\n' $var1
[hello]
[world]


var1内部的var1上的单词拆分,等效于$()

$ printf '[%s]\n' "$var1"
[hello     world]


没有单词拆分echo "hello" "world",不引用var1没问题:

$ var2=$(echo $var1)
$ printf '[%s]\n' "$var2"
[hello world]


再次在$()上拆分单词:

$ var2=$(echo "$var1")
$ printf '[%s]\n' "$var2"
[hello     world]


引用这两种方式是最简单的确定方法。

$ var2="$(echo $var1)"
$ printf '[%s]\n' "$var2"
[hello world]


全局问题

不引用变量也可以导致其内容的全局扩展:

$ var2="$(echo "$var1")"
$ printf '[%s]\n' "$var2"
[hello     world]


请注意,这种情况仅在变量被扩展后发生。在分配过程中不必引用全局名称:

$ mkdir test; cd test; touch file1 file2
$ var="*"
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]


使用var1禁用此行为: />和set -f重新启用它:

$ var=*
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]


评论


人们往往会忘记单词拆分不是唯一的问题,您可能想要更改示例以使var1 ='hello * world'也能说明问题。

–StéphaneChazelas
2014年6月6日15:15

#3 楼

除了已接受的答案之外,

虽然我通常同意@ l0b0的回答,但我怀疑在“最差”列表中放置空反引号至少部分是由于假设$(...)随处可见。我意识到这个问题指定了Bash,但是很多时候Bash的意思是/bin/sh,但实际上并不一定总是完整的Bourne Again外壳。不知道如何处理$(...),因此声称与之兼容的脚本(例如,通过#!/bin/sh的shebang行)如果它们实际上是由“真实的” /bin/sh运行的,则可能会出现错误的行为-当说,生成init脚本或打包前脚本和后脚本,并且可以在基本系统安装过程中将其放到一个令人惊讶的位置。

如果有任何听起来像您打算做的事情使用此变量,嵌套可能比实际运行脚本要担心的要少。当这是一个简单的案例并且需要考虑可移植性时,即使我希望脚本通常在/bin/sh是Bash的系统上运行,出于这个原因,我也经常倾向于使用反引号,并使用多个分配而不是嵌套。

说了这么多,实现$(...)的无处不在的shell(Bash,Dash等)使我们处于一个很好的位置,坚持使用更漂亮,更易于嵌套且最近更喜欢的POSIX语法。在大多数情况下,由于所有原因@ l0b0都提及。

此外:这在StackOverflow上也偶尔出现–


命令替换:反引号或美元符号/括号括起来? [重复](2012年2月)
shell编程:$(command)和`command`有什么区别(2011年1月)


评论


//,极好的答案。过去,我也遇到过/ bin / sh的向后兼容性问题。您对如何使用向后兼容方法处理他的问题有任何建议吗?

– Nathan Basanese
16年1月7日在23:10

以我的经验来看:有时您可能需要将反引号更改为用圆括号括起来的美元对数,而从来没有一次(具体来说是必要的)将美元对数转换为用引号括起来的。我只在JavaScript代码中写反引号,而在Shell代码中不写反引号。

–陆even
19年5月31日在16:43