Bagaimana saya bisa menemukan semua ekstensi file yang berbeda di hierarki folder?

235

Pada mesin Linux saya ingin melintasi hierarki folder dan mendapatkan daftar semua ekstensi file yang berbeda di dalamnya.

Apa cara terbaik untuk mencapai ini dari shell?

GloryFish
sumber

Jawaban:

347

Coba ini (tidak yakin apakah itu cara terbaik, tetapi berhasil):

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

Ini berfungsi sebagai berikut:

  • Temukan semua file dari folder saat ini
  • Mencetak ekstensi file jika ada
  • Buat daftar diurutkan unik
Ivan Nevostruev
sumber
8
hanya untuk referensi: jika Anda ingin mengecualikan beberapa direktori dari pencarian (misalnya .svn), gunakan find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u sumber
Dennis Golomazov
Spasi tidak akan membuat perbedaan. Setiap nama file akan berada di baris terpisah, jadi pembatas daftar file akan "\ n" bukan spasi.
Ivan Nevostruev
1
Pada Windows, ini berfungsi lebih baik dan jauh lebih cepat daripada menemukan: dir / s / b | perl -ne 'cetak $ 1 jika m /\.([^^.\\+++$/' | sort -u
Ryan Shillington
8
Variasi, ini menunjukkan daftar dengan jumlah per ekstensi:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout
55

Tidak perlu pipa untuk sort, awk dapat melakukan semuanya:

find . -type f | awk -F. '!a[$NF]++{print $NF}'
SiegeX
sumber
Saya tidak mendapatkan ini berfungsi sebagai alias, saya mulai awk: kesalahan sintaks pada konteks baris 1 sumber >>>! A [] << awk: bailing out pada baris sumber 1. Apa yang saya lakukan salah? Alias ​​saya didefinisikan seperti ini: alias file_ext = "find. -Type f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152
2
@ user2602152 masalahnya adalah bahwa Anda mencoba mengelilingi seluruh satu-liner dengan tanda kutip untuk aliasperintah tetapi perintah itu sendiri sudah menggunakan tanda kutip dalam perintah temukan. Untuk memperbaiki ini saya akan menggunakan bashsintaks string literal sebagai berikut:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX
ini tidak berfungsi jika satu subdir memiliki. di namanya dan file tidak memiliki ekstensi file. Contoh: ketika kita lari dari maindir, itu akan gagal untukmaindir/test.dir/myfile
Nelson Teixeira
1
@NelsonTeixeira Tambahkan -printf "%f\n"ke akhir perintah 'temukan' dan jalankan kembali tes Anda.
SiegeX
41

Versi rekursif:

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

Jika Anda ingin total (berapa kali ekstensi terlihat):

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

Non-rekursif (folder tunggal):

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

Saya sudah mendasarkan ini pada posting forum ini , kredit harus pergi ke sana.

ChristopheD
sumber
Bagus! juga berfungsi untuk skenario git saya, sedang mencoba mencari tahu jenis file yang telah saya sentuh dalam komit terakhir:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
vulcan raven
30

Powershell:

dir -recurse | select-object extension -unique

Terima kasih kepada http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html

Simon R
sumber
20
OP mengatakan "On a Linux machine"
Forbesmyester
9
sebenarnya ada prowershell untuk linux keluar sekarang: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC
4
Seperti yang ditulis, ini juga akan mengambil direktori yang ada .di dalamnya (mis. jquery-1.3.4Akan muncul seperti .4pada output). Ubah untuk dir -file -recurse | select-object extension -uniquehanya mendapatkan ekstensi file.
mcw
1
@Forbesmyester: Orang dengan Windows (seperti saya) akan menemukan pertanyaan ini. Jadi ini berguna.
Roel
1
Terima kasih atas jawaban Powershell. Anda tidak berasumsi bagaimana pengguna mencari. Banyak orang yang diangkat karena suatu alasan
Mahesh
20

Alternatif awk-less, sed-less, Perl-less, Python-less POSIX-compliant saya:

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

Kuncinya adalah membalik garis dan memotong ekstensi di awal.
Ini juga mengubah ekstensi menjadi huruf kecil.

Contoh output:

   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
Ondra Žižka
sumber
di mac, uniqtidak memiliki bendera lengkap --count, tetapi -cberfungsi dengan baik
worc
12

Temukan semuanya dengan sebuah titik dan tunjukkan hanya sufiks.

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

jika Anda tahu semua sufiks memiliki 3 karakter, maka

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

atau dengan sed menunjukkan semua sufiks dengan satu hingga empat karakter. Ubah {1,4} ke rentang karakter yang Anda harapkan di akhiran.

find . -type f | sed -n 's/.*\.\(.\{1,4\}\)$/\1/p'| sort -u
pengguna224243
sumber
1
Tidak perlu pipa untuk 'mengurutkan', awk dapat melakukan semuanya: temukan. -type f -name " . " | awk -F. '! a [$ NF] ++ {print $ NF}'
SiegeX
@ SiegeX Hormat Anda harus menjadi jawaban yang terpisah. Ia menemukan bahwa perintah untuk bekerja terbaik untuk folder besar, karena ia mencetak ekstensi ketika menemukannya. Tetapi perhatikan bahwa itu harus: -nama " . "
Ralf
@Ralf selesai, diposting jawaban di sini . Tidak begitu yakin tentang apa yang Anda maksud dengan -name "."hal itu karena memang sudah seperti itu
SiegeX
Maksud saya seharusnya -nama "*. *", Tetapi StackOverflow menghapus karakter *, yang mungkin terjadi dalam komentar Anda juga.
Ralf
Sepertinya ini harus menjadi jawaban yang diterima, awk lebih disukai daripada perl sebagai alat baris perintah dan itu merangkul filosofi unix untuk memipipkan program kecil yang dapat dioperasikan ke dalam prosedur yang kohesif dan mudah dibaca.
Jon z
7

Menambahkan variasi saya sendiri ke dalam campuran. Saya pikir itu yang paling sederhana dari yang banyak dan bisa berguna ketika efisiensi bukan masalah besar.

find . -type f | grep -o -E '\.[^\.]+$' | sort -u
gkb0986
sumber
1
+1 untuk portabilitas, meskipun regex cukup terbatas, karena hanya cocok dengan ekstensi yang terdiri dari satu huruf. Menggunakan regex dari jawaban yang diterima tampaknya lebih baik:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu
1
Sepakat. Saya sedikit malas di sana. Mengedit jawaban saya untuk memperbaiki kesalahan yang Anda lihat.
gkb0986
keren. Saya mengutip kutipan untuk menggandakan, memperbarui graries dan dependensi grep (karena disediakan dengan git sudah usang) dan sekarang ini bekerja di bawah windows. merasa seperti pengguna linux.
msangel
5

Dalam Python menggunakan generator untuk direktori yang sangat besar, termasuk ekstensi kosong, dan mendapatkan berapa kali setiap ekstensi muncul:

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)
Andres Restrepo
sumber
5

Saya mencoba banyak jawaban di sini, bahkan jawaban "terbaik". Mereka semua kekurangan apa yang saya cari secara spesifik. Jadi selain 12 jam terakhir duduk dalam kode regex untuk beberapa program dan membaca dan menguji jawaban-jawaban ini, inilah yang saya hasilkan dengan cara yang persis seperti yang saya inginkan.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Menemukan semua file yang mungkin memiliki ekstensi.
  • Greps hanya ekstensi
  • Greps untuk ekstensi file antara 2 dan 16 karakter (sesuaikan angka jika tidak sesuai dengan kebutuhan Anda). Ini membantu menghindari file cache dan file sistem (bit file sistem untuk mencari jail).
  • Awk untuk mencetak ekstensi dalam huruf kecil.
  • Sortir dan bawa hanya nilai unik. Awalnya saya telah mencoba mencoba jawaban awk tetapi itu akan menggandakan item cetak yang bervariasi dalam hal sensitivitas.

Jika Anda memerlukan hitungan ekstensi file, gunakan kode di bawah ini

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

Meskipun metode ini akan membutuhkan waktu untuk diselesaikan dan mungkin bukan cara terbaik untuk menyelesaikan masalah, mereka bekerja.

Pembaruan: Per @ alpha_989 ekstensi file yang panjang akan menyebabkan masalah. Itu karena regex asli "[[: alpha:]] {3,6}". Saya telah memperbarui jawaban untuk menyertakan regex "[[: alpha:]] {2,16}". Namun siapa pun yang menggunakan kode ini harus menyadari bahwa angka-angka itu adalah min dan maks dari berapa lama ekstensi diizinkan untuk hasil akhir. Apa pun di luar rentang itu akan dipisah menjadi beberapa baris dalam output.

Catatan: Posting asli tidak membaca "- Greps untuk ekstensi file antara 3 dan 6 karakter (sesuaikan angka jika tidak sesuai dengan kebutuhan Anda). Ini membantu menghindari file cache dan file sistem (bit file sistem adalah untuk mencari penjara). "

Ide: Dapat digunakan untuk menemukan ekstensi file dengan panjang tertentu melalui:

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

Di mana 4 adalah panjang ekstensi file untuk disertakan dan kemudian temukan juga ekstensi di luar panjang itu.

Shinrai
sumber
Apakah versi hitungan rekursif?
Fernando Montoya
@ Shinrai, Secara umum berfungsi dengan baik. tetapi jika Anda memiliki beberapa ekstensi file acak yang sangat panjang seperti .download, itu akan memecah ".download" menjadi 2 bagian dan melaporkan 2 file yang "downlo" dan yang lain "ad"
alpha_989
@ alpha_989, Itu karena regex "[[: alpha:]] {3,6}" juga akan menyebabkan masalah dengan ekstensi yang lebih kecil dari 3 karakter. Sesuaikan dengan yang Anda butuhkan. Secara pribadi saya akan mengatakan 2,16 harus bekerja dalam banyak kasus.
Shinrai
Terima kasih telah membalas .. Ya .. itulah yang saya sadari nanti. Ini bekerja dengan baik setelah saya memodifikasinya mirip dengan apa yang Anda sebutkan.
alpha_989
3

Karena sudah ada solusi lain yang menggunakan Perl:

Jika Anda memasang Python, Anda juga bisa melakukannya (dari 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
sumber
2

Tidak ada jawaban sejauh ini yang berurusan dengan nama file dengan baris baru dengan benar (kecuali untuk ChristopheD, yang baru saja masuk saat saya mengetik ini). Berikut ini bukan shell satu-liner, tetapi bekerja, dan cukup cepat.

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

sumber
2

Saya pikir ini belum disebutkan:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c
Dmitry B.
sumber
Ini mungkin akan sangat lambat karena melahirkan proses baru untuk setiap file.
Ondra Žižka
1

Saya pikir cara yang paling sederhana & mudah adalah

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

Itu dimodifikasi pada cara 3 ChristopheD.

Robert
sumber
0

Anda juga bisa melakukan ini

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

Saya telah menemukannya sederhana dan cepat ...

   # 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
Diego Callejo
sumber
0

Jawaban yang diterima menggunakan REGEX dan Anda tidak dapat membuat perintah alias dengan REGEX, Anda harus memasukkannya ke dalam skrip shell, saya menggunakan Amazon Linux 2 dan melakukan hal berikut:

  1. Saya memasukkan kode jawaban yang diterima ke dalam file menggunakan:

    sudo vim find.sh

tambahkan kode ini:

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

simpan file dengan mengetik: :wq!

  1. sudo vim ~/.bash_profile

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

  3. :wq!

  4. . ~/.bash_profile

Chris Medina
sumber