如何在文件夹层次结构中找到所有不同的文件扩展名?


235

在Linux机器上,我想遍历文件夹层次结构并获取其中所有不同文件扩展名的列表。

从外壳实现这一目标的最佳方法是什么?

Answers:


347

试试这个(不确定这是否是最好的方法,但是可以用):

find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u

它的工作方式如下:

  • 查找当前文件夹中的所有文件
  • 打印文件扩展名(如果有)
  • 制作唯一的排序列表

8
仅供参考:如果您想从搜索中排除某些目录(例如.svn),请使用find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u 来源
Dennis Golomazov

空格不会有任何区别。每个文件名将在单独的行中,因此文件列表定界符将为“ \ n”而不是空格。
Ivan Nevostruev

1
在Windows上,这比find效果更好,并且速度更快:dir / s / b | perl -ne'如果m / \则打印$ 1。([[^^。\\\\] +)$ /'| -u
Ryan Shillington


8
一种变体,它显示具有每个扩展名计数的列表:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout

54

不需要使用管道sort,awk可以完成所有操作:

find . -type f | awk -F. '!a[$NF]++{print $NF}'

我没有让它作为别名工作,而是得到awk:源代码行1上下文中的语法错误是>>>!a [] <<< awk:在源代码行1出现故障。我在做什么错?我的别名定义如下:alias file_ext =“ find。-type f -name' '| awk -F。'!a [$ NF] ++ {print $ NF}'”
2602152 2015年

2
@ user2602152问题是您试图用alias命令引号将整个单行括起来,但是命令本身已经在find命令中使用了引号。要解决此问题,我将使用bash的原义字符串语法如下:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX 2015年

如果一个子目录包含一个。的名称,并且文件没有文件扩展名。例如:当我们从maindir运行时,它会失败maindir/test.dir/myfile
Nelson Teixeira

1
@NelsonTeixeira -printf "%f\n"在'find'命令的末尾添加并重新运行测试。
SiegeX

41

递归版本:

find . -type f | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u

如果您想要总计(查看扩展名的次数):

find . -type f | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort | uniq -c | sort -rn

非递归(单个文件夹):

for f in *.*; do printf "%s\n" "${f##*.}"; done | sort -u

我基于此论坛帖子,信誉应该放在那儿。


大!也适用于我的git场景,试图找出我在上一次提交中接触过的文件类型:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
vulcan raven

30

电源外壳:

dir -recurse | select-object extension -unique

感谢http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html


20
OP说“在Linux机器上”
Forbesmyester

9
实际上,现在已经有适用于Linux的产品:github.com/Microsoft/PowerShell-DSC-for-Linux
KIC

4
如所写,这还将选择其中包含a的目录.(例如,jquery-1.3.4.4在输出中显示)。更改为dir -file -recurse | select-object extension -unique仅获取文件扩展名。
mcw

1
@Forbesmyester:Windows的人(像我一样)会发现这个问题。所以这很有用。
Roel

1
感谢Powershell的回答。您不假设用户如何搜索。许多人出于某种原因进行了投票
Mahesh

20

我的无awk,无sed,无Perl,无Python的POSIX兼容替代方案:

find . -type f | rev | cut -d. -f1 | rev  | tr '[:upper:]' '[:lower:]' | sort | uniq --count | sort -rn

诀窍是,它会反转行并在开始处剪切扩展名。
还将扩展名转换为小写。

输出示例:

   3689 jpg
   1036 png
    610 mp4
     90 webm
     90 mkv
     57 mov
     12 avi
     10 txt
      3 zip
      2 ogv
      1 xcf
      1 trashinfo
      1 sh
      1 m4v
      1 jpeg
      1 ini
      1 gqv
      1 gcs
      1 dv

在Mac上,uniq没有完整的标志--count,但-c效果很好
世界纪录

12

用点找到所有内容,仅显示后缀。

find . -type f -name "*.*" | awk -F. '{print $NF}' | sort -u

如果您知道所有后缀都有3个字符,则

find . -type f -name "*.???" | awk -F. '{print $NF}' | sort -u

或带有sed的所有后缀都显示一到四个字符。将{1,4}更改为后缀中期望的字符范围。

find . -type f | sed -n 's/.*\.\(.\{1,4\}\)$/\1/p'| sort -u

1
无需对管道进行“排序”,awk可以完成所有操作:find。类型的f-name“ ” | awk -F '!a [$ NF] ++ {print $ NF}'
SiegeX

@SiegeX您应该单独回答。它发现该命令最适合大型文件夹,因为它会在找到扩展名时打印扩展名。但是请注意,它应该是:-name“
Ralf

@Ralf完成了,在这里发布了答案。不太确定您所指的是什么意思,-name "."因为那已经是事实了
SiegeX

我的意思是它应该是-name“ *。*”,但是StackOverflow会删除*字符,这也可能发生在您的注释中。
拉尔夫

看来这应该是一个可以接受的答案,awk比perl更适合作为命令行工具,并且它包含将小型可互操作程序管道化为具有凝聚力和可读性的程序的unix原理。
乔恩

7

在混合中添加我自己的变体。我认为这是最简单的方法,当效率不是一个大问题时,它可能会很有用。

find . -type f | grep -o -E '\.[^\.]+$' | sort -u

1
+1表示可移植性,尽管正则表达式非常有限,因为它仅匹配由单个字母组成的扩展名。从接受的答案中使用正则表达式似乎更好:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu 2013年

1
同意 我在那里放松了一下。编辑我的答案以纠正您发现的错误。
gkb0986 2013年

凉。我将引号改为双引号,更新grep 二进制文件和依赖项(因为git提供的版本已过时),现在在Windows下可以正常工作。感觉像linux用户。
msangel 2015年

5

在Python中,使用生成器来处理非常大的目录(包括空白扩展名),并获取每个扩展名出现的次数:

import json
import collections
import itertools
import os

root = '/home/andres'
files = itertools.chain.from_iterable((
    files for _,_,files in os.walk(root)
    ))
counter = collections.Counter(
    (os.path.splitext(file_)[1] for file_ in files)
)
print json.dumps(counter, indent=2)

5

我在这里尝试了很多答案,甚至是“最佳”答案。他们都没有达到我的要求。因此,除了过去12个小时坐在多个程序的正则表达式代码中并阅读和测试这些答案之外,这也是我想到的,其工作原理与我想要的完全一样。

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • 查找所有可能具有扩展名的文件。
  • 只抓住扩展名
  • 请注意2到16个字符之间的文件扩展名(如果不合适,请调整数字)。这有助于避免缓存文件和系统文件(系统文件位用于监禁)。
  • Awk以小写形式打印扩展名。
  • 排序并仅输入唯一值。最初,我曾尝试尝试awk答案,但它会使打印出来的项目加倍,以区分大小写。

如果您需要计算文件扩展名,请使用以下代码

find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort | uniq -c | sort -rn

尽管这些方法需要一些时间才能完成,并且可能不是解决问题的最佳方法,但它们确实有效。

更新:每个@ alpha_989长文件扩展名将导致问题。这是由于原始正则表达式“ [[:::]] {3,6}”。我已经将答案更新为包括正则表达式“ [[:alpha:]] {2,16}”。但是,使用此代码的任何人都应该注意,这些数字是最终输出允许扩展多长时间的最小值和最大值。超出此范围的任何内容都将在输出中分成多行。

注意:原始文章确实显示为“-表示3到6个字符之间的文件扩展名(如果不符合您的需要,请调整数字)。这有助于避免缓存文件和系统文件(系统文件位是在监狱中搜索)。 ”

想法:可用于通过以下方式查找特定长度的文件扩展名:

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{4,}" | awk '{print tolower($0)}' | sort -u

其中4是要包括的文件扩展名长度,然后查找超出该长度的任何扩展名。


计数版本是否递归?
费尔南多·蒙托亚

@Shinrai,总的来说效果很好。但是,如果您有一些非常长的随机文件扩展名(例如.download),它将把“ .download”分为两部分,并报告2个文件,其中一个是“ downlo”,另一个是“ ad”
alpha_989

@ alpha_989,这是由于正则表达式“ [[[:alpha:]] {3,6}”也会导致扩展名少于3个字符的问题。根据您的需要进行调整。我个人认为2,16在大多数情况下应该有效。
井'18

感谢您的回复..是的,那是我后来才意识到的。我修改了类似于您提到的内容后,它运行良好。
alpha_989 '18

3

由于已经有了使用Perl的另一种解决方案:

如果您安装了Python,也可以(从外壳程序)执行以下操作:

python -c "import os;e=set();[[e.add(os.path.splitext(f)[-1]) for f in fn]for _,_,fn in os.walk('/home')];print '\n'.join(e)"

2

到目前为止,没有一个答复正确地使用换行符来处理文件名(除了ChristopheD的文件名之外,我在键入此文件时才出现该文件名)。以下内容不是shell的单行代码,而是有效的,并且相当快。

import os, sys

def names(roots):
    for root in roots:
        for a, b, basenames in os.walk(root):
            for basename in basenames:
                yield basename

sufs = set(os.path.splitext(x)[1] for x in names(sys.argv[1:]))
for suf in sufs:
    if suf:
        print suf

2

我认为还没有提到这一点:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c

由于为每个文件生成一个新进程,这可能会很慢。
OndraŽižka,

1

我认为最简单直接的方法是

for f in *.*; do echo "${f##*.}"; done | sort -u

它是根据ChristopheD的第三种方式修改的。


0

你也可以这样做

find . -type f -name "*.php" -exec PATHTOAPP {} +

0

我发现它简单快捷...

   # find . -type f -exec basename {} \; | awk -F"." '{print $NF}' > /tmp/outfile.txt
   # cat /tmp/outfile.txt | sort | uniq -c| sort -n > tmp/outfile_sorted.txt

0

接受的答案使用REGEX,并且您无法使用REGEX创建别名命令,必须将其放入外壳脚本中,我使用的是Amazon Linux 2,并执行以下操作:

  1. 我使用以下命令将接受的答案代码放入文件中:

    须藤vim find.sh

添加此代码:

find ./ -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u

通过输入以下内容保存文件: :wq!

  1. sudo vim ~/.bash_profile

  2. alias getext=". /path/to/your/find.sh"

  3. :wq!

  4. . ~/.bash_profile

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.