循环遍历所有具有特定扩展名的文件

for i in $(ls);do
if [ $i = '*.java' ];then
echo "I do something with the file $i"
fi
done

我想循环遍历当前文件夹中的每个文件,并检查它是否匹配特定的扩展名。上面的代码不起作用,你知道为什么吗?

162590 次浏览

不需要花哨的把戏:

for i in *.java; do
[ -f "$i" ] || break
...
done

保护程序确保如果没有匹配的文件,循环将退出,而不会尝试处理不存在的文件名 *.java

bash(或者支持类似功能的 shell)中,可以使用 nullglob选项 简单地忽略失败的匹配,而不进入循环体。

shopt -s nullglob
for i in *.java; do
...
done

在评论中有关 break-vs-continue讨论的更多细节。我认为无论使用 break还是 continue都有点超出范围,因为第一个循环试图区分两种情况:

  1. *.java没有匹配项,因此被视为文本。
  2. *.java至少有一个匹配项,该匹配项可能包含一个名为 *.java的条目。

在第1种情况下,break没有问题,因为没有其他 $i的值,而且 breakcontinue将是等价的(尽管我发现 break更加明确; 您正在退出循环,而不仅仅是等待循环被动地退出)。

在情况 # 2中,您仍然必须对任何可能的匹配执行必要的筛选。因此,break或者 continue的选择与 测试(-f-d-e等)的相关性要小于 $i的相关性,IMO 认为 $i是一个错误的选择,无法判断你是否在一开始就“错误地”进入了循环。

也就是说,我不想在案例 # 1中检查 $i 完全没有的值,在案例 # 2中,你对这个值所做的更多的是与每个文件的业务逻辑有关,而不是首先要处理的 选择文件的逻辑。我更愿意将这个逻辑留给单独的用户,而不是在问题中表达一个或另一个选择。


顺便说一句,zsh提供了一种方法,可以在 Globb 本身中执行这种过滤。可以匹配以 .java结尾的 只有常规文件(并禁用将不匹配的模式作为错误而不是作为文本处理的默认行为)

for f in *.java(.N); do
...
done

有了上面的代码,您就是 保证,如果您到达循环体,那么 $f就扩展为一个常规文件的名称。.使得 *.java只匹配常规文件,而 N导致失败的匹配扩展为零,而不是产生错误。

还有其他类似的 全球限定词用于对文件名扩展执行各种过滤。(我喜欢开玩笑说,zsh的 global 扩展完全取代了使用 find的需要。)

正如@chepner 在他的评论中所说,你是在将 $i 比作一个固定的字符串。

要展开并纠正这种情况,应该使用[[]]和 regex 操作符 = ~

例如:

for i in $(ls);do
if [[ $i =~ .*\.java$ ]];then
echo "I want to do something with the file $i"
fi
done

右边的正则表达式是根据左边操作符的值测试的,不应该用引号(引号不会出错,但是会与一个固定的字符串进行比较,因此很可能会失败)

但是@chepner 上面使用 globb 的答案是一个更有效的机制。

正确答案是“ Chepner’s”

EXT=java
for i in *.${EXT}; do
...
done

然而,这里有一个检查文件名是否有给定扩展名的小技巧:

EXT=java
for i in *; do
if [ "${i}" != "${i%.${EXT}}" ];then
echo "I do something with the file $i"
fi
done

我同意其他关于循环文件的正确方法的回答,但是 OP 问:

上面的代码不起作用,你知道为什么吗?

太棒了!

一篇优秀的文章 [和[ ? ]的区别是什么详细解释了在其他差异中,您不能在 test命令中使用 expression matchingpattern matching(这是 [的简写)



Feature            new test [[    old test [           Example


Pattern matching    = (or ==)    (not available)    [[ $name = a* ]] || echo "name does not start with an 'a': $name"


Regular Expression     =~        (not available)    [[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!"
matching

这就是你的脚本失败的原因。如果 OP 对使用 [[语法的答案感兴趣(它的缺点是在很多平台上都不支持 [命令) ,我很乐意编辑我的答案以包含它。

编辑: 任何有关如何将答案中的数据格式化为表格的提示都会很有帮助!

递归地添加子文件夹,

for i in `find . -name "*.java" -type f`; do
echo "$i"
done

循环遍历以 .img.bin.txt后缀结尾的所有文件,并打印文件名:

for i in *.img *.bin *.txt;
do
echo "$i"
done

或者采用递归方式(也可以在所有子目录中查找) :

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
echo "$i"
done

我发现这个解决方案非常方便,它使用了 find中的 -or选项:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

它将找到扩展名为 texpngpdf的文件。