Làm cách nào tôi có thể tìm thấy tất cả các phần mở rộng tệp riêng biệt trong hệ thống phân cấp thư mục?


234

Trên máy Linux, tôi muốn duyệt qua hệ thống phân cấp thư mục và nhận danh sách tất cả các phần mở rộng tệp riêng biệt trong đó.

Điều gì sẽ là cách tốt nhất để đạt được điều này từ một cái vỏ?

Câu trả lời:


346

Hãy thử điều này (không chắc đó là cách tốt nhất, nhưng nó hoạt động):

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

Nó hoạt động như sau:

  • Tìm tất cả các tập tin từ thư mục hiện tại
  • In phần mở rộng của tập tin nếu có
  • Tạo một danh sách được sắp xếp duy nhất

8
chỉ để tham khảo: nếu bạn muốn loại trừ một số thư mục khỏi tìm kiếm (ví dụ .svn), hãy sử dụng find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u nguồn
Dennis Golomazov

Không gian sẽ không làm cho bất kỳ sự khác biệt. Mỗi tên tệp sẽ nằm trong một dòng riêng biệt, vì vậy dấu phân cách danh sách tệp sẽ là "\ n" không phải là khoảng trắng.
Ivan Nevostruev

1
Trên Windows, điều này hoạt động tốt hơn và nhanh hơn nhiều so với find: dir / s / b | perl -ne 'in $ 1 nếu m /\.([ ^ ^ ^. sort -u
Ryan Shillington

3
biến thể git của câu trả lời: sử dụng git ls-tree -r HEAD --name-onlythay vìfind
jakub.g

8
Một biến thể, điều này hiển thị danh sách với số lượng trên mỗi phần mở rộng:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout

54

Không cần đường ống đến sort, awk có thể làm tất cả:

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

Tôi không làm cho nó hoạt động như một bí danh, tôi nhận được awk: lỗi cú pháp ở bối cảnh dòng nguồn 1 là >>>! A [] <<< awk: bảo lãnh tại dòng nguồn 1. Tôi đang làm gì sai? Bí danh của tôi được định nghĩa như thế này: alias file_ext = "find. -Ape f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152

2
@ user2602152 vấn đề là bạn đang cố gắng bao quanh toàn bộ một lớp lót bằng dấu ngoặc kép cho aliaslệnh nhưng bản thân lệnh đã sử dụng dấu ngoặc kép trong lệnh find. Để khắc phục điều này, tôi sẽ sử dụng bashcú pháp chuỗi bằng chữ như sau:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX 14/03/2015

điều này không hoạt động nếu một subir có a. trong tên của nó và tập tin không có phần mở rộng tập tin. Ví dụ: khi chúng tôi chạy từ maindir, nó sẽ thất bại chomaindir/test.dir/myfile
Nelson Teixeira

1
@NelsonTeixeira Thêm -printf "%f\n"vào cuối lệnh 'find' và chạy lại bài kiểm tra của bạn.
Cuộc bao vây

41

Phiên bản đệ quy:

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

Nếu bạn muốn tổng số (số lần mở rộng được nhìn thấy):

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

Không đệ quy (thư mục đơn):

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

Tôi đã dựa trên bài đăng trên diễn đàn này , tín dụng sẽ đến đó.


Tuyệt quá! cũng hoạt động cho kịch bản git của tôi, đã cố gắng tìm ra loại tệp nào tôi đã chạm vào lần xác nhận cuối cùng:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
Vulcan raven

30

Quyền hạn:

dir -recurse | select-object extension -unique

Cảm ơn http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html


20
OP cho biết "Trên máy Linux"
Forbesmyester

9
thực sự có prowershell cho linux ngay bây giờ: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC

4
Như đã viết, điều này cũng sẽ chọn các thư mục có .trong đó (ví dụ: jquery-1.3.4sẽ hiển thị như .4trong đầu ra). Thay đổi để dir -file -recurse | select-object extension -uniquechỉ nhận phần mở rộng tập tin.
mcw

1
@Forbesmyester: Những người có Windows (như tôi) sẽ tìm thấy câu hỏi này. Vì vậy, điều này là hữu ích.
Roel

1
Cảm ơn câu trả lời của Powershell. Bạn không giả định cách người dùng tìm kiếm. Rất nhiều người ủng hộ vì một lý do
Mahesh

20

Giải pháp thay thế phù hợp với POSIX của tôi, ít hơn, ít sử dụng, không có Perl, không có Python:

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

Thủ thuật là nó đảo ngược dòng và cắt phần mở rộng ở đầu.
Nó cũng chuyển đổi các phần mở rộng thành chữ thường.

Ví dụ đầu ra:

   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

trên mac, uniqkhông có đầy đủ cờ --count, nhưng -cchỉ hoạt động tốt
worc

12

Tìm everythin với một dấu chấm và chỉ hiển thị hậu tố.

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

nếu bạn biết tất cả hậu tố có 3 ký tự thì

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

hoặc với sed hiển thị tất cả các hậu tố có từ một đến bốn ký tự. Thay đổi {1,4} thành phạm vi các ký tự bạn mong đợi trong hậu tố.

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

1
Không cần đường ống để 'sắp xếp', awk có thể làm tất cả: tìm. -type f -name " . " | ôi -F. '! a [$ NF] ++ {in $ NF}'
Cuộc bao vây

@SiegeX Bạn nên là một câu trả lời riêng biệt. Nó tìm thấy lệnh đó để làm việc tốt nhất cho các thư mục lớn, vì nó in các phần mở rộng khi nó tìm thấy chúng. Nhưng lưu ý rằng nó phải là: -name " . "
Ralf

@Ralf xong, đăng câu trả lời tại đây . Không hoàn toàn chắc chắn về ý nghĩa của -name "."bạn bởi vì đó là những gì nó đã có
SiegeX

Ý tôi là nó phải là -name "*. *", Nhưng StackOverflow sẽ xóa các ký tự *, có lẽ cũng đã xảy ra trong bình luận của bạn.
Ralf

Có vẻ như đây là câu trả lời được chấp nhận, awk thích hợp hơn là perl như một công cụ dòng lệnh và nó bao hàm triết lý unix của việc đưa các chương trình nhỏ có thể tương tác vào các thủ tục gắn kết và dễ đọc.
Jon z

7

Thêm biến thể của riêng tôi vào hỗn hợp. Tôi nghĩ đó là cách đơn giản nhất trong số rất nhiều và có thể hữu ích khi hiệu quả không phải là mối quan tâm lớn.

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

1
+1 cho tính di động, mặc dù regex khá hạn chế, vì nó chỉ khớp với các phần mở rộng bao gồm một chữ cái duy nhất. Sử dụng regex từ câu trả lời được chấp nhận có vẻ tốt hơn:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu

1
Đã đồng ý. Tôi chùng bước một chút ở đó. Chỉnh sửa câu trả lời của tôi để sửa lỗi bạn phát hiện.
gkb0986

mát mẻ. Tôi chenge trích dẫn để doublequote, cập nhật giraries và phụ thuộc grep (vì được cung cấp với git đã lỗi thời) và bây giờ công việc này dưới cửa sổ. cảm thấy như người dùng linux.
msangel

5

Trong Python sử dụng trình tạo cho các thư mục rất lớn, bao gồm các tiện ích mở rộng trống và nhận được số lần mỗi tiện ích mở rộng hiển thị:

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

Tôi đã thử một loạt các câu trả lời ở đây, ngay cả câu trả lời "tốt nhất". Tất cả họ đã nghĩ ra những gì tôi đặc biệt là sau đó. Vì vậy, ngoài 12 giờ ngồi mã regex cho nhiều chương trình và đọc và kiểm tra những câu trả lời này, đây là những gì tôi nghĩ ra, nó hoạt động CHÍNH XÁC như tôi muốn.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Tìm tất cả các tệp có thể có phần mở rộng.
  • Greps chỉ phần mở rộng
  • Greps cho phần mở rộng tập tin từ 2 đến 16 ký tự (chỉ cần điều chỉnh số nếu chúng không phù hợp với nhu cầu của bạn). Điều này giúp tránh các tệp bộ đệm và tệp hệ thống (bit tệp hệ thống là để tìm kiếm tù).
  • Awk để in các phần mở rộng trong trường hợp thấp hơn.
  • Sắp xếp và chỉ mang lại các giá trị duy nhất. Ban đầu tôi đã cố gắng thử câu trả lời awk nhưng nó sẽ nhân đôi các mục in khác nhau tùy theo độ nhạy.

Nếu bạn cần số lượng phần mở rộng tập tin thì hãy sử dụng đoạn mã dưới đây

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

Mặc dù các phương pháp này sẽ mất một chút thời gian để hoàn thành và có lẽ không phải là cách tốt nhất để giải quyết vấn đề, chúng vẫn hoạt động.

Cập nhật: Phần mở rộng tệp dài Per @ alpha_989 sẽ gây ra sự cố. Đó là do regex ban đầu "[[: alpha:]] {3,6}". Tôi đã cập nhật câu trả lời để bao gồm regex "[[: alpha:]] {2,16}". Tuy nhiên, bất cứ ai sử dụng mã này nên lưu ý rằng những con số đó là tối thiểu và tối đa của thời gian mở rộng được phép cho đầu ra cuối cùng. Bất cứ điều gì ngoài phạm vi đó sẽ được chia thành nhiều dòng trong đầu ra.

Lưu ý: Bài đăng gốc đã đọc "- Greps cho phần mở rộng tệp từ 3 đến 6 ký tự (chỉ cần điều chỉnh số nếu chúng không phù hợp với nhu cầu của bạn). Điều này giúp tránh các tệp bộ đệm và tệp hệ thống (bit tệp hệ thống là để tìm kiếm tù). "

Ý tưởng: Có thể được sử dụng để tìm các phần mở rộng tệp trong một độ dài cụ thể thông qua:

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

Trong đó 4 là độ dài phần mở rộng tệp để bao gồm và sau đó tìm bất kỳ tiện ích mở rộng nào ngoài độ dài đó.


Là phiên bản đếm đệ quy?
Fernando Montoya

@Shinrai, Nói chung hoạt động tốt. nhưng nếu bạn có một số phần mở rộng tệp ngẫu nhiên thực sự dài như .doad, nó sẽ chia ".doad" thành 2 phần và báo cáo 2 tệp là "downlo" và phần khác là "quảng cáo"
alpha_989

@ alpha_989, Đó là do regex "[[: alpha:]] {3,6}" cũng sẽ gây ra sự cố với tiện ích mở rộng nhỏ hơn 3 ký tự. Điều chỉnh theo những gì bạn cần. Cá nhân tôi muốn nói rằng 2,16 nên hoạt động trong hầu hết các trường hợp.
Shinrai

Cảm ơn bạn đã trả lời .. Vâng .. đó là những gì tôi nhận ra sau này. Nó hoạt động tốt sau khi tôi sửa đổi nó tương tự như những gì bạn đề cập.
alpha_989

3

Vì đã có một giải pháp khác sử dụng Perl:

Nếu bạn đã cài đặt Python, bạn cũng có thể thực hiện (từ trình bao):

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

Cho đến nay, không có câu trả lời nào liên quan đến tên tập tin với các dòng mới (ngoại trừ của BarsheD, vừa xuất hiện khi tôi đang gõ cái này). Dưới đây không phải là lớp vỏ một lớp, nhưng hoạt động, và khá nhanh.

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

Tôi không nghĩ rằng cái này đã được đề cập đến:

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

Điều này có thể sẽ khá chậm do tạo ra một quy trình mới cho mỗi tệp.
Ondra Žižka

1

Tôi nghĩ rằng cách đơn giản và đơn giản nhất là

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

Nó đã được sửa đổi theo cách thứ 3 của BarsheD.


0

bạn cũng có thể làm điều này

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

0

Tôi đã tìm thấy nó đơn giản và nhanh chóng ...

   # 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

Câu trả lời được chấp nhận sử dụng REGEX và bạn không thể tạo lệnh bí danh với REGEX, bạn phải đặt nó vào tập lệnh shell, tôi đang sử dụng Amazon Linux 2 và đã làm như sau:

  1. Tôi đặt mã câu trả lời được chấp nhận vào một tệp bằng cách sử dụng:

    sudo vim find.sh

thêm mã này:

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

lưu tệp bằng cách gõ: :wq!

  1. sudo vim ~/.bash_profile

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

  3. :wq!

  4. . ~/.bash_profile

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.