我正试图用bash脚本循环遍历指定扩展名列表中的文件。我尝试了在使用for循环匹配具有各种扩展名的文件时给出的解决方案,但它并没有按预期工作。给出的解决方案是:
for file in "${arg}"/*.{txt,h,py}; do
这是我的版本:
for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu}
do
echo "$f"
done
当我在一个有epub
文件的目录中运行它时,我得到:
/*.epub
/*.mobi
/*.chm
/*.rtf
/*.lit
/*.djvu
所以我尝试更改for
语句:
for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu}
然后我得到了:
089281098X.epub
*.mobi
*.chm
*.rtf
*.lit
*.djvu
我也得到了相同的结果:
for f in *.{epub,mobi,chm,rtf,lit,djvu}
因此,似乎"${arg}"
论证是不必要的。
尽管这两条语句中的任何一条都可以找到指定扩展名的文件并将其传递给程序,但我会从未解析的*.
文件名中得到读取错误。
我在OS X Mountain Lion
上运行这个。我知道默认的bash shell已经过时了,所以我使用homebrew
将其从3.2.48
升级到4.2.45
,看看这是否是问题所在。这并没有起到什么作用,所以我想知道为什么我会得到这些意想不到的结果。给定的解决方案是错误的,还是OS X
bash shell在某种程度上与*NIX
版本不同?是否有其他方法可以在OS X
bash shell中更好地完成同样的任务?
这可能是BASH 4.2主义。它在我的BASH中不起作用,它仍然是3.2。但是,如果使用shopt -s extglob
,则可以使用*(...)
:
shopt -s extglob
for file in *.*(epub|mobi|chm|rtf|lit|djvu)
do
...
done
@David W.:在中为f的shopt-s extglob(epub|mobi|chm|rtf|lit|djvu)导致:089281098X.epub@kojiro:arg=。shopt为"${arg}"/中的f设置nullglob。{epub,mobi,chm,rtf,lit,djvu}结果为:/089281098X.epub shopt-"${arg}">中f的null glob。{epub,mobi,chm,rtf,lit,djvu}的结果是:089281098X.epub所以所有这些变体都有效,但我不明白为什么。你们中的任何一个人能解释一下每个变体是怎么回事吗?${arg}在做什么?我真的很想了解这一点,这样我就能增加我的知识。谢谢你的帮助。
在矿中:
for f in *.*(epub|mobi|chm|rtf|lit|djvu)
我没有包括${arg}
,它扩展到$arg
的值。*(...)
与括号中的模式匹配,括号是一系列扩展中的一个。因此,它与*.epub
相匹配。
Kojiro的:
arg=.
shopt -s nullglob
for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu}
在他的匹配中包括$arg
和斜杠。因此,koriro从./
开始,因为这正是他们所要求的。
这就像之间的区别
echo *
和
echo ./*
顺便说一句,你也可以用其他表达式来做这件事:
echo *.*(epub|mobi|chm|rtf|lit|djvu)
shell正在为您进行所有的扩展。它实际上与for
语句本身无关。
一个glob必须扩展到一个现有的、找到的名称,或者它被单独留下,星号保持不变。如果您有一个空目录,*.foo
将扩展到*.foo
。(除非您使用nullglob
Bash扩展。)
代码的问题在于,您从一个arg$arg
开始,它显然是空的或未定义的。因此,您的glob${arg}/*.epub
扩展为/*.epub
,因为根目录中没有以".epub"结尾的文件。它从不在当前目录中查找。为此,您需要先设置arg=.
。
在第二个示例中,${arg}*.epub
确实展开了,因为$arg
是空的,但其他文件不存在,因此它们继续不作为glob展开。正如我之前所暗示的,一个简单的解决方法是用shopt -s nullglob
激活nullglob
。这是特定于bash的,但如果没有匹配的文件,则会导致*.foo
扩展为空字符串。对于严格的POSIX解决方案,您必须使用[ -f "$f" ]
过滤掉未扩展的globs。(再说一遍,如果你想要POSIX,你也不能使用大括号扩展。)
总之,最好的解决方案是使用(最直观、最优雅):
shopt -s extglob
for f in *.*(epub|mobi|chm|rtf|lit|djvu)
或者,与引用线程中给出的原始解决方案保持一致(如上所述是错误的):
shopt -s nullglob
for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu}
这应该做到:
for file in $(find ./ -name '*.epub' -o -name '*.mobi' -o -name '*.chm' -o -name '*.rtf' -o -name '*.lit' -o -name '*.djvu'); do
echo $file
done