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

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

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

152919 次浏览

试试这个(不确定是不是最好的方法,但确实有效):

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

它的工作原理如下:

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

递归版本:

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

我已经基于这个论坛帖子,信用应该去那里。

找到每一个点,只显示后缀。

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

如果你知道所有后缀有3个字符,那么

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

或使用sed显示所有1到4个字符的后缀。将{1,4}更改为您希望在后缀中使用的字符范围。

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

因为已经有了另一个使用Perl的解决方案:

如果你安装了Python,你还可以这样做(从shell):

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)"

到目前为止,没有一个回复正确地处理带有换行符的文件名(除了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

Powershell:

dir -recurse | select-object extension -unique

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

不需要sort的管道,awk可以做所有的事情:

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

在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)

你也可以这样做

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

加入我自己的变化。我认为这是最简单的,当效率不是一个大问题的时候,它是有用的。

find . -type f | grep -oE '\.(\w+)$' | sort -u

我在这里试了很多答案,甚至是“最好的”。的答案。他们都没有达到我的要求。所以除了过去12个小时坐在多个程序的正则表达式代码中,阅读和测试这些答案之外,这就是我想出的工作方式,完全像我想要的那样。

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • 查找可能具有扩展名的所有文件。
  • Greps只有扩展
  • 2到16个字符之间的文件扩展名(如果它们不符合您的需要,只需调整数字)。这有助于避免缓存文件和系统文件(系统文件位是搜索jail)。
  • Awk以小写打印扩展名。
  • 排序并只带来唯一的值。最初,我试图尝试awk的答案,但它会双打印项目,不同的大小写敏感性。

如果你需要文件扩展名的计数,那么使用下面的代码

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

虽然这些方法需要一些时间才能完成,而且可能不是解决问题的最佳方法,但它们是有效的。

< p >更新: 每个@alpha_989长的文件扩展名将导致一个问题。这是由于最初的正则表达式&;[[:alpha:]]{3,6}"。我已经更新了答案,包括正则表达式&;[[:alpha:]]{2,16}&;然而,任何使用这段代码的人都应该知道,这些数字是最终输出所允许的扩展长度的最小值和最大值。任何超出该范围的内容将在输出中被分割成多行

注意:原来的帖子读的是3到6个字符之间的文件扩展名的"- Greps(如果它们不适合你的需要,只需调整数字)。这有助于避免缓存文件和系统文件(系统文件位是搜索监狱)。

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

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

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

我认为最简单的&简单的方法是

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

它是根据克里斯托夫的第三种方法改进的。

我想这个还没有被提到:

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

我的无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

我发现它简单快捷……

   # 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

接受的答案使用REGEX,你不能用REGEX创建别名命令,你必须把它放在一个shell脚本中,我使用亚马逊Linux 2,并做了以下工作:

  1. 我把接受的答案代码放入一个文件,使用:

    Sudo vim find.sh

    .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

另一种方法:

find . -type f -name "*.*" -printf "%f\n" | while IFS= read -r; do echo "${REPLY##*.}"; done | sort -u

你可以删除-name "*.*",但这确保我们只处理具有某种扩展名的文件。

-printffind的print,而不是bash。-printf "%f\n"只打印文件名,剥离路径(并添加换行符)。

然后使用字符串替换,使用${REPLY##*.}删除到最后一个点。

注意,$REPLY只是read的内置变量。我们也可以用自己的形式:while IFS= read -r file,这里$file是变量。