在Bash环境中,我使用包含空格的变量,并在命令替换中使用这些变量。不幸的是,我找不到关于SE的答案。
引用变量的正确方法是什么?如果将它们嵌套,该怎么办?
DIRNAME=$(dirname "$FILE")
还是在外面引用
DIRNAME="$(dirname $FILE)"
还是两者?
DIRNAME="$(dirname "$FILE")"
还是使用反引号?
DIRNAME=`dirname "$FILE"`
什么是正确的方法?以及如何轻松检查引号设置是否正确?
#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
评论
另请参见-unix.stackexchange.com/questions/97560/…另请参见什么时候需要双引号?和$ VAR与$ {VAR}并引用或不引用
这是一个很好的问题,但是考虑到所有与嵌入式毛坯有关的问题,为什么您会故意使用它们而使自己的生活变得艰难?
@Joe,带空格的空白是指文件名中的空格吗?我个人并不经常使用它们,但是我正在与其他人的目录和文件一起工作,我不确定它们是否包含空格。此外,我认为最好一次完成它,这样我以后就不必担心。
是。我们要和那些“其他人”做什么?