Come posso trovare tutte le estensioni di file distinte in una gerarchia di cartelle?


235

Su una macchina Linux vorrei attraversare una gerarchia di cartelle e ottenere un elenco di tutte le estensioni di file distinte al suo interno.

Quale sarebbe il modo migliore per ottenere questo da una shell?

Risposte:


347

Prova questo (non sono sicuro che sia il modo migliore, ma funziona):

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

Funziona come segue:

  • Trova tutti i file dalla cartella corrente
  • Stampa l'estensione dei file, se presente
  • Crea un elenco ordinato univoco

8
solo per riferimento: se si desidera escludere alcune directory dalla ricerca (ad esempio .svn), utilizzare la find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u fonte
Dennis Golomazov,

Gli spazi non faranno alcuna differenza. Ogni nome di file sarà in una riga separata, quindi il delimitatore dell'elenco dei file sarà "\ n" non spazio.
Ivan Nevostruev,

1
Su Windows, funziona meglio ed è molto più veloce di find: dir / s / b | perl -ne 'stampa $ 1 se m /\.([^^.\\\\[+)$/' | sort -u
Ryan Shillington il

3
modifica variazione della risposta: usa git ls-tree -r HEAD --name-onlyinvece difind
jakub.g

8
Una variante, mostra l'elenco con i conteggi per estensione:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout,

55

Non c'è bisogno che la pipe sortlo faccia, Awk può fare tutto:

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

Non riesco a farlo funzionare come alias, sto ottenendo awk: l'errore di sintassi nel contesto della riga 1 di origine è >>>! A [] <<< awk: salvataggio nella riga di origine 1. Cosa sto facendo di sbagliato? Il mio alias è definito in questo modo: alias file_ext = "find. -Type f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152

2
@utente2602152 il problema è che si sta tentando di racchiudere l'intera aliasriga con le virgolette per il comando ma il comando stesso utilizza già le virgolette nel comando find. Per risolvere questo problema, vorrei usare bashla sintassi della stringa letterale in questo modo:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX

questo non funziona se un sottodir ha un. nel suo nome e il file non ha estensione. Esempio: quando scappiamo da Maindir fallirà permaindir/test.dir/myfile
Nelson Teixeira il

1
@NelsonTeixeira Aggiungi -printf "%f\n"alla fine del comando 'trova' ed esegui nuovamente il test.
SiegeX,

41

Versione ricorsiva:

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

Se vuoi i totali (come mai è stata vista l'estensione):

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

Non ricorsivo (cartella singola):

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

Ho basato questo su questo post sul forum , il merito dovrebbe andare lì.


Grande! funziona anche per il mio scenario git, stava cercando di capire quale tipo di file ho toccato nell'ultimo commit:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
vulcan raven

30

PowerShell:

dir -recurse | select-object extension -unique

Grazie a http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html


20
L'OP disse "Su una macchina Linux"
Forbesmyester il

9
attualmente è disponibile prowershell per Linux: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC

4
Come scritto, questo raccoglierà anche le directory che hanno un .in loro (ad esempio jquery-1.3.4verrà mostrato come .4nell'output). Passare a dir -file -recurse | select-object extension -uniqueper ottenere solo le estensioni dei file.
Mc

1
@Forbesmyester: le persone con Windows (come me) troveranno questa domanda. Quindi questo è utile.
Roel

1
Grazie per la risposta Powershell. Non supponi come gli utenti effettuano ricerche. Molte persone hanno votato per un motivo
Mahesh

20

La mia alternativa compatibile con POSIX senza imbarazzo, senza sed, senza Perl, senza Python:

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

Il trucco è che inverte la linea e taglia l'estensione all'inizio.
Converte anche le estensioni in lettere minuscole.

Esempio di 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

su Mac, uniqnon ha la bandiera completa --count, ma -cfunziona benissimo
worc

12

Trova tutto con un punto e mostra solo il suffisso.

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

se sai che tutti i suffissi hanno 3 caratteri allora

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

o con sed mostra tutti i suffissi con uno o quattro caratteri. Cambia {1,4} nell'intervallo di caratteri che ti aspetti nel suffisso.

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

1
Non è necessario che il pipe si "classifichi", awk può fare tutto: find. -type f -name " . " | awk -F. '! a [$ NF] ++ {print $ NF}'
SiegeX il

@SiegeX La tua dovrebbe essere una risposta separata. Ha trovato quel comando per funzionare al meglio per cartelle di grandi dimensioni, poiché stampa le estensioni mentre le trova. Ma nota che dovrebbe essere: -name " . "
Ralf

@Ralf, risposta postata qui . Non sono del tutto sicuro di cosa intendi per -name "."cosa, perché è quello che è già
SiegeX

Volevo dire che dovrebbe essere -name "*. *", Ma StackOverflow rimuove i * caratteri, cosa che probabilmente è accaduta anche nel tuo commento.
Ralf,

Sembra che questa dovrebbe essere la risposta accettata, awk è preferibile perl come strumento da riga di comando e abbraccia la filosofia unix di convogliare piccoli programmi interoperabili in procedure coerenti e leggibili.
Jon z,

7

Aggiungendo la mia variazione al mix. Penso che sia il più semplice del lotto e possa essere utile quando l'efficienza non è una grande preoccupazione.

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

1
+1 per la portabilità, sebbene il regex sia piuttosto limitato, poiché corrisponde solo alle estensioni costituite da una sola lettera. Usare il regex dalla risposta accettata sembra migliore:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu

1
Concordato. Mi sono rilassato un po 'lì. Modifica la mia risposta per correggere l'errore che hai notato.
gkb0986,

freddo. Chenge citazioni a doublequotes, aggiornamento grep biraries e dipendenze (perché fornito con git è obsoleto) e ora questo lavoro sotto Windows. sento come un utente Linux.
msangel

5

In Python usare generatori per directory molto grandi, incluse estensioni vuote, e ottenere il numero di volte in cui ogni estensione viene visualizzata:

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

Ho provato un sacco di risposte qui, anche la risposta "migliore". Sono venuti tutti a corto di quello che stavo cercando. Quindi, oltre alle ultime 12 ore di seduta nel codice regex per più programmi e leggere e testare queste risposte, questo è quello che ho trovato che funziona ESATTAMENTE come voglio.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Trova tutti i file che possono avere un'estensione.
  • Greps solo l'estensione
  • Greps per estensioni di file tra 2 e 16 caratteri (basta regolare i numeri se non si adattano alle tue necessità). Ciò consente di evitare file di cache e file di sistema (il bit del file di sistema serve per eseguire la ricerca nel jail).
  • Awk per stampare le estensioni in minuscolo.
  • Ordina e porta solo valori univoci. Inizialmente avevo tentato di provare la risposta awk ma avrebbe raddoppiato gli elementi di stampa che variavano in base alla distinzione tra maiuscole e minuscole.

Se è necessario un conteggio delle estensioni di file, utilizzare il codice seguente

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

Sebbene questi metodi richiederanno del tempo per essere completati e probabilmente non sono i modi migliori per risolvere il problema, funzionano.

Aggiornamento: Per @ alpha_989 le estensioni di file lunghe causeranno un problema. Ciò è dovuto alla regex originale "[[: alpha:]] {3,6}". Ho aggiornato la risposta per includere il regex "[[: alpha:]] {2,16}". Tuttavia, chiunque utilizzi questo codice dovrebbe essere consapevole del fatto che quei numeri sono il minimo e il massimo di quanto tempo è consentito l'estensione per l'output finale. Qualunque cosa al di fuori di tale intervallo verrà suddivisa in più righe nell'output.

Nota: il post originale ha letto "- Greps per estensioni di file tra 3 e 6 caratteri (basta regolare i numeri se non si adattano alle tue necessità). Questo aiuta a evitare file di cache e file di sistema (il bit del file di sistema è di cercare jail). "

Idea: potrebbe essere utilizzato per trovare estensioni di file su una lunghezza specifica tramite:

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

Dove 4 è la lunghezza delle estensioni dei file da includere e quindi trovare anche eventuali estensioni oltre quella lunghezza.


La versione di conteggio è ricorsiva?
Fernando Montoya,

@Shinrai, in generale funziona bene. ma se hai delle estensioni di file casuali che sono davvero lunghe come .download, spezzerà il ".download" in 2 parti e segnalerà 2 file uno che è "downlo" e un altro che è "annuncio"
alpha_989

@ alpha_989, Ciò è dovuto alla regex "[[: alpha:]] {3,6}" causerà anche un problema con estensioni inferiori a 3 caratteri. Adatta a ciò di cui hai bisogno. Personalmente direi che 2,16 dovrebbe funzionare nella maggior parte dei casi.
Shinrai,

Grazie per la risposta .. Sì .. questo è quello che ho realizzato in seguito. Ha funzionato bene dopo averlo modificato in modo simile a quello che hai menzionato.
alpha_989

3

Poiché esiste già un'altra soluzione che utilizza Perl:

Se hai installato Python potresti anche fare (dalla 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)"

2

Nessuna delle risposte finora tratta correttamente i nomi dei file con le nuove righe (tranne che per ChristopheD, che è appena arrivato mentre scrivevo). Quanto segue non è una shell one-liner, ma funziona ed è ragionevolmente veloce.

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

Non credo che questo sia stato ancora menzionato:

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

Ciò sarebbe probabilmente piuttosto lento a causa della generazione di un nuovo processo per ciascun file.
Ondra Žižka,

1

Penso che il modo più semplice e diretto sia

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

È stato modificato sulla terza via di ChristopheD.


0

potresti anche farlo

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

0

L'ho trovato semplice e veloce ...

   # 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

La risposta accettata utilizza REGEX e non puoi creare un comando alias con REGEX, devi inserirlo in uno script di shell, sto usando Amazon Linux 2 e ho fatto quanto segue:

  1. Ho inserito il codice di risposta accettato in un file usando:

    sudo vim find.sh

aggiungi questo codice:

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

salva il file digitando: :wq!

  1. sudo vim ~/.bash_profile

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

  3. :wq!

  4. . ~/.bash_profile

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.