for i in $(ls);do
if [ $i = '*.java' ];then
echo "I do something with the file $i"
fi
done
我想循环遍历当前文件夹中的每个文件,并检查它是否与特定扩展名匹配。上面的代码不起作用,你知道为什么吗?
不需要花哨的技巧:
for i in *.java; do
[ -f "$i" ] || break
...
done
守卫确保在没有匹配文件的情况下,循环将退出而不会尝试处理不存在的文件名*.java
。
在(或支持类似功能的shell)中,您可以使用nullglob
选项来忽略匹配失败并且不进入循环体。
shopt -s nullglob
for i in *.java; do
...
done
针对评论中break
和continue
的讨论,以下提供更多细节。我认为使用break
还是continue
并不重要,因为第一个循环试图区分两种情况:
*.java
没有匹配项,因此视为字面文本。*.java
至少有一个匹配项,该匹配项可能包括一个名为*.java
的条目。在情况#1中,使用break
是可以的,因为没有其他的$i
值即将到来,而break
和continue
是等效的(尽管我认为break
更显式;你正在退出循环,而不仅仅是等待循环被动退出)。
在情况#2中,您仍需要对任何可能的匹配进行必要的过滤。因此,选择break
或continue
的选择比适用于$i
的哪个测试(-f
、-d
、-e
等)不那么相关,而根据我个人经验,这种选择方法并不正确,因为这首先是确定是否“错误地”进入循环的方法。
也就是说,在情况#1中,我不希望检查$i
的值,并且在情况#2中,您对该值所做的操作更多地依赖于每个文件的业务逻辑,而不是首先选择要处理的文件的逻辑。我更愿意将这种逻辑留给各自的用户,而不是在问题中表达一种选择或另一种选择。
另外,zsh
提供了一种在glob本身中执行此类过滤的方法。您可以只匹配以.java
结尾的常规文件(并禁用默认行为,即将未匹配的模式视为错误,而不是字面文本),如下:
for f in *.java(.N); do
...
done
有了上述内容,您可以确保如果您到达循环体,则$f
会扩展为常规文件的名称。点号(.
)使*.java
仅匹配常规文件,而N
会导致匹配失败时不会产生错误,而是扩展为空。
还有其他类似的通配符限定词用于对文件名扩展进行各种过滤。 (我喜欢开玩笑说,zsh
的通配符扩展完全取代了使用find
的需要。)
for i in *.java *.cpp; do
。如果你在 bash
中启用了扩展模式 (shopt -s extglob
),你可以写成 for i in *.@(java|cpp); do
。 - chepnershopt -s nullglob
命令,这样不匹配的模式会扩展为空序列而不是按字面处理。 - chepner[ -f "$i" ] || break
, we need [ -f "$i" ] || continue
, right?” 可以翻译为:我们需要将“[ -f "$i" ] || break
”改为“[ -f "$i" ] || continue
”,对吗? - codeforestercontinue
是必需的。如果有一个名为 b.java
的普通文件和一个名为 a.java
的目录,那么在循环到达 b.java
之前可以通过 break
终止循环。 - nekketsuuu递归添加子文件夹,
for i in `find . -name "*.java" -type f`; do
echo "$i"
done
"$i"
。 - tripleee.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
man find
:-o
(表示逻辑或) - Timo正确答案是@chepner的
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
ext
代替.java
会怎样呢? - AR89正如@chepner在他的评论中所说,您正在将$i与一个固定字符串进行比较。
为了扩展和纠正这种情况,您应该使用[[ ]]与正则表达式运算符=~
例如:
for i in $(ls);do
if [[ $i =~ .*\.java$ ]];then
echo "I want to do something with the file $i"
fi
done
在=~右侧的正则表达式将针对左操作数的值进行测试,并且不应该被引用。 (引用不会出错,但将与固定字符串进行比较,因此很可能会失败)
但是,@chepner上面的答案使用glob是一个更有效的机制。
ext
代替.java
会怎样? - AR89for i in $(ls -lR); do ...
在子目录中进行搜索,或者如果您想要文件的相对路径:for i in $(find -L .);do ...
借助另一个好答案的帮助:https://dev59.com/03VD5IYBdhLWcg3wDXF3#105249 - Mahdad Baghani特征 新的test [[ 旧的test [ 示例因此这就是您的脚本失败的原因。如果OP对使用[[语法(缺点是没有像[命令一样被多个平台支持)的答案感兴趣,我很乐意编辑我的答案来包括它。
模式匹配 =(或==) (不可用) [[ $name = a* ]] || echo "name does not start with an 'a': $name"
正则表达式匹配 =~ (不可用) [[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!"
我发现这个解决方案非常方便,它使用了find
命令中的-or
选项:
find . -name \*.tex -or -name "*.png" -or -name "*.pdf"
此命令将查找扩展名为tex
、png
和pdf
的文件。
for i in $(ls *.java); do echo "do something with file $i"; done
呢?(该命令会遍历当前目录下所有扩展名为.java
的文件,并对每个文件执行一些操作) - speakr$i
与字面字符串“*.java”进行比较;这里不执行模式扩展。 - chepnerif [[ $i == *.java ]]; then
..(请注意双重[[]]和未引用的*.java)。 - that other guyls
-- 接受 @chepner 的答案。 - glenn jackman