递归查找具有特定扩展名的文件


436

我正在尝试使用bash(最新的Ubuntu LTS版本)在目录及其子目录中找到具有特定扩展名的所有文件。

这是在脚本文件中写的:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

不幸的是,当我在终端中启动此脚本时,它说:

[: 29: in: unexpected operator

(使用$extension代替'in'

这是怎么回事,哪里有错误?但是这个花括号


2
错误是由于缺少一个“ {”
shrewmouse

Answers:


749
find $directory -type f -name "*.in"

比整个过程要短一些(并且更安全-处理文件名和目录名中的空格)。

您的脚本可能因.名称中没有“ a”的条目而失败,将其$extension清空。


16
是的,find默认情况下是递归的。您可以根据需要限制深度(请参见手册页)。

1
我想将所有找到的文件作为参数传递给jar文件。如何执行?
翻转

8
@flip:这是一个不同的问题。发布一个新问题,详细说明您想要做的事情以及到目前为止您已经尝试过的事情。

一个小小的修正:使用'* .in'或\ *。in而不是“ * .in”,因为双引号不会阻止shell扩展。也就是说,如果当前目录中有扩展名为.in的文件,则脚本将无法正常工作。
Shnatsel

4
@Shnatsel:双引号确实可以防止外壳扩展。试试看。


60

我使用的语法与@Matt建议的语法有点不同:

find $directory -type f -name \*.in

(这是少一击)。


1
如果当前目录中有一个扩展名为.in的文件,那么Matt的脚本也将不起作用,而您的脚本仍然可以工作。参见stackoverflow.com/questions/5927369/…–
Shnatsel

4
@Shnatsel此评论(以及您的评论)是完全错误的。
gniourf_gniourf 2015年

1
@gniourf_gniourf您应该为您的声明提供一些参考,否则您可能会争辩说:“不,您错了”。但实际上您是对的:gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel,2016年

@ user1885518:我认为应该由那些声称脚本不起作用的人提供一些脚本失败的示例。这就是我在脚本损坏的地方留下注释的方式:通常是关于引号和文件名,其中包含空格,换行符,glob等,并且我特别解释了为什么损坏了。
gniourf_gniourf

2
提供参考始终是讨论中的好方法,并不取决于谁是第一个。他应该,你应该。
Murmel

14

不使用find

du -a $directory | awk '{print $2}' | grep '\.in$'

3
grep这里并没有必要。awk具有正则表达式,可以将其输出限制为与模式匹配的值。
Kenster

如果您要经历100 TB的数据量,则此方法非常有用。查找命令需要太多时间来处理。这将立即开始。
Protonova'2

1
awk|grep是反模式。让awk做一下grepping。
詹斯

10
  1. 还有一个是{后失踪browsefolders ()
  2. 一切$in都应该$suffix
  3. 与的联系cut仅使您成为的中间部分front.middle.extension。您应该在${varname%%pattern}和朋友上阅读Shell手册。

我假设您这样做是作为Shell脚本练习,否则find已经提出的解决方案是解决之道。

要在不运行脚本的情况下检查shell语法是否正确,请使用sh -n scriptname



7

尽管find在这里使用命令可能很有用,但Shell本身提供了实现此要求的选项,而无需任何第三方工具。该bash外壳提供了使用扩展的水珠支持选项,您可以在递归路径获得的文件名那场比赛你想要的扩展。

扩展选项extglob需要使用以下shopt选项进行设置。这些选项在-s支持下启用,在他-u标志下禁用。另外,您可以使用更多的选项nullglob,例如,将不匹配的glob完全清除,替换为一组零单词。并globstar允许通过的所有目录递归

shopt -s extglob nullglob globstar

现在,您所需要做的就是形成glob表达式,以包含某个扩展名的文件,您可以按以下方式进行操作。我们使用数组来填充全局结果,因为当正确地引用和扩展它们时,带有特殊字符的文件名将保持不变,并且不会由于外壳拆分单词而损坏。

例如列出*.csv递归路径中的所有文件

fileList=(**/*.csv)

该选项**是递归遍历子文件夹,并且*.csv是全局扩展以包括提到的扩展名的任何文件。现在要打印实际文件,只需执行

printf '%s\n' "${fileList[@]}"

在shell脚本中使用数组并进行正确的带引号的扩展是正确的方法,但是对于交互使用,您可以简单地ls将glob表达式与

ls -1 -- **/*.csv

可以很好地扩展它以匹配多个文件,即以多个扩展名结尾的文件(即类似于在find命令中添加多个标志)。例如,考虑需要让所有的递归图像文件的情况下,即扩展的*.gif*.png并且*.jpg,所有你需要IS

ls -1 -- **/+(*.jpg|*.gif|*.png)

这很可能会扩展为也具有否定结果。使用相同的语法,可以使用全局结果排除某些类型的文件。假设您想排除具有上述扩展名的文件名,则可以

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

该构造!()是一个否定运算,不包括内部列出的任何文件扩展名,并且|是一个替换运算符,就像在Extended Regular Expressions库中用于对glob进行OR匹配一样。

请注意,这些扩展的glob支持在POSIX bourne shell中不可用,并且它仅适用于.NET的最新版本bash。因此,如果您正在考虑跨POSIX和bashshell 运行的脚本的可移植性,那么此选项将不合适。




By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.