find .
和/home/user/*
作为命令输入有什么区别for
?例如:
for var in $(find .)
do echo "$var"
done
或者
for var in /home/user/*
do
echo "$var"
done
在第一种情况下,for
命令会分解名称包含空格的文件。而在第二种情况下则不然。为什么?
答案1
这是 shell 的标准做法。操作的顺序是命令替换 ( $(find .)
),然后分词,然后全局扩展 ( /home/user/*
)。
来自POSIX标准(单词分割 = 字段分割;全局扩展 = 路径名扩展):
单词扩展的顺序如下:
应从头到尾执行波形符扩展(请参阅波形符扩展)、参数扩展(请参阅参数扩展)、命令替换(请参阅命令替换)和算术扩展(请参阅算术扩展)。请参阅令牌识别中的第 5 项。
除非 IFS 为空,否则应对步骤 1 生成的字段部分执行字段拆分(请参阅字段拆分)。
除非 set -f 有效,否则应执行路径名扩展(请参阅路径名扩展)。
报价删除(请参阅报价删除)应始终最后执行。
因此,始终建议尽可能使用 glob,以便分词不会干扰您的文件名。该$(find)
构造实际上是一个示例Bash 陷阱 #1。
答案2
shell 按顺序做事。 $(find .)
称为命令替换。命令替换的结果受:
- 分词,
- 路径名扩展
- 报价删除
当文件名带有空格时,分词是导致问题的原因。
/home/user/*
是路径名扩展。请注意,这是上面列表中的倒数第二个。它仅受报价删除的影响。
答案3
两者之间的另一个区别是 shell glob(如/home/user/*
)通常不包含“隐藏文件”(以点开头的文件名)。另一方面,find
将匹配除特殊目录“.”之外的所有文件名。和“..”(当前目录和父目录)。
答案4
解释它的另一种方法:当*
使用时,shell 知道单个元素是什么。输出find
只是一个长字符串。 shell 不知道元素(及其分隔符)是什么。
命令替换可以以特殊方式与产生带引号的输出的程序一起使用;那么问题就不会出现。但是为了使 shell 识别您需要的引用,例如,eval
这通常会使整个表达式变得更加复杂:
eval for var in $(ls --quoting-style=shell)\; do echo '"$var"'\; done