295

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

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

4

17 回答 17

418

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

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

它的工作原理如下:

  • 查找当前文件夹中的所有文件
  • 打印文件的扩展名(如果有)
  • 制作一个唯一的排序列表
于 2009-12-03T19:21:46.710 回答
74

不需要管道到sort,awk 可以做到这一切:

find . -type f | awk -F. '!a[$NF]++{print $NF}'
于 2011-08-24T05:21:12.500 回答
55

递归版本:

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

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

于 2009-12-03T19:38:02.323 回答
40

我的 awk-less、sed-less、Perl-less、Python-less 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
于 2019-03-23T18:37:54.427 回答
38

电源外壳:

dir -recurse | select-object extension -unique

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

于 2010-04-23T14:18:42.800 回答
13

用点查找所有内容并仅显示后缀。

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
于 2009-12-03T21:47:59.100 回答
11

将我自己的变体添加到组合中。我认为这是最简单的,当效率不是一个大问题时它会很有用。

find . -type f | grep -oE '\.(\w+)$' | sort -u
于 2013-07-15T05:59:06.233 回答
9

我在这里尝试了一堆答案,甚至是“最佳”答案。他们都没有达到我特别追求的目标。因此,除了过去 12 小时坐在多个程序的正则表达式代码中并阅读和测试这些答案之外,这就是我想出的,它完全符合我的要求。

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • 查找所有可能具有扩展名的文件。
  • 仅对扩展名进行 Greps
  • Greps 用于 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 长文件扩展名会导致问题。这是由于原始的正则表达式“[[:alpha:]]{3,6}”。我已更新答案以包含正则表达式“[[:alpha:]]{2,16}”。但是,任何使用此代码的人都应该知道,这些数字是最终输出允许扩展多长时间的最小值和最大值。该范围之外的任何内容都将在输出中拆分为多行。

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

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

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

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

于 2014-05-26T18:45:50.370 回答
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)
于 2012-08-24T19:17:28.120 回答
4

由于已经有另一个使用 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)"
于 2009-12-04T08:27:53.573 回答
2

到目前为止,没有一个回复正确地处理带有换行符的文件名(除了 ChristopheD's,它只是在我输入这个时出现的)。以下不是 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
于 2009-12-04T08:35:28.977 回答
2

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

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

它是在 ChristopheD 的第三种方式上修改的。

于 2018-02-13T08:21:45.380 回答
2

我认为还没有提到这个:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c
于 2018-05-21T23:01:17.007 回答
2

另一种方式:

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

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

-printfis的find打印,而不是 bash。-printf "%f\n"仅打印文件名,去除路径(并添加换行符)。

然后我们使用字符串替换来删除最后一个点${REPLY##*.}

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

于 2021-05-31T15:18:57.407 回答
1

接受的答案使用 REGEX,您无法使用 REGEX 创建别名命令,您必须将其放入 shell 脚本中,我使用的是 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

于 2020-04-04T13:02:38.330 回答
0

you could also do this

find . -type f -name "*.php" -exec PATHTOAPP {} +
于 2013-03-25T16:12:15.643 回答
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
于 2020-02-20T14:28:44.337 回答