Wie finde ich alle unterschiedlichen Dateierweiterungen in einer Ordnerhierarchie?

234

Auf einem Linux-Computer möchte ich eine Ordnerhierarchie durchlaufen und eine Liste aller darin enthaltenen Dateierweiterungen erhalten.

Was wäre der beste Weg, um dies aus einer Shell heraus zu erreichen?

GloryFish
quelle

Antworten:

345

Versuchen Sie dies (nicht sicher, ob es der beste Weg ist, aber es funktioniert):

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

Es funktioniert wie folgt:

  • Finden Sie alle Dateien aus dem aktuellen Ordner
  • Druckt ggf. die Dateierweiterung
  • Erstellen Sie eine eindeutige sortierte Liste
Ivan Nevostruev
quelle
8
Nur als Referenz: Wenn Sie einige Verzeichnisse von der Suche ausschließen möchten (z. B. .svn), verwenden Sie find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u source
Dennis Golomazov
Leerzeichen machen keinen Unterschied. Jeder Dateiname steht in einer separaten Zeile, sodass das Dateilisten-Trennzeichen "\ n" und kein Leerzeichen ist.
Ivan Nevostruev
1
Unter Windows funktioniert dies besser und ist viel schneller als find: dir / s / b | perl -ne 'print $ 1 if m /\.([^^.\\\\‹+)$/' | sort -u
Ryan Shillington
8
Eine Variation, dies zeigt die Liste mit Zählungen pro Erweiterung:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
Marcovtwout
54

Keine Notwendigkeit für das Rohr sort, awk kann alles:

find . -type f | awk -F. '!a[$NF]++{print $NF}'
SiegeX
quelle
Ich bekomme dies nicht als Alias, ich bekomme awk: Syntaxfehler in Quellzeile 1 Kontext ist >>>! A [] <<< awk: Rettung in Quellzeile 1. Was mache ich falsch? Mein Alias ​​ist wie folgt definiert: Alias ​​file_ext = "find. -Type f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152
2
@ user2602152 Das Problem ist, dass Sie versuchen, den gesamten Einzeiler mit Anführungszeichen für den aliasBefehl zu umgeben, der Befehl selbst jedoch bereits Anführungszeichen im Befehl find verwendet. Um dies zu beheben, würde ich die bashwörtliche String-Syntax folgendermaßen verwenden:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX
Dies funktioniert nicht, wenn ein Unterverzeichnis ein hat. in seinem Namen und die Datei hat keine Dateierweiterung. Beispiel: Wenn wir von Maindir weglaufen, wird es fürmaindir/test.dir/myfile
Nelson Teixeira
1
@NelsonTeixeira Fügen Sie -printf "%f\n"am Ende des Befehls 'find' hinzu und führen Sie Ihren Test erneut aus.
SiegeX
41

Rekursive Version:

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

Wenn Sie Summen möchten (wie oft wurde die Erweiterung gesehen):

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

Nicht rekursiv (einzelner Ordner):

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

Ich habe dies auf diesem Forumbeitrag basiert , Kredit sollte dort gehen.

ChristopheD
quelle
Toll! Funktioniert auch für mein Git-Szenario. Ich habe versucht herauszufinden, welche Art von Dateien ich beim letzten Commit berührt habe:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
Vulkanischer Rabe
30

Power Shell:

dir -recurse | select-object extension -unique

Vielen Dank an http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html

Simon R.
quelle
20
Das OP sagte "Auf einem Linux-Computer"
Forbesmyester
9
Derzeit gibt es eine Prowershell für Linux: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC
4
Wie geschrieben, werden dadurch auch Verzeichnisse mit einem .in aufgenommen (z. B. jquery-1.3.4wie .4in der Ausgabe). Wechseln Sie zu dir -file -recurse | select-object extension -unique, um nur Dateierweiterungen zu erhalten.
mcw
1
@Forbesmyester: Leute mit Windows (wie ich) werden diese Frage finden. Das ist also nützlich.
Roel
1
Vielen Dank für die Antwort von Powershell. Sie gehen nicht davon aus, wie Benutzer suchen. Viele Leute haben aus einem bestimmten Grund gestimmt
Mahesh
20

Meine awk-less, sed-less, Perl-less, Python-less POSIX-kompatible Alternative:

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

Der Trick ist, dass es die Linie umkehrt und die Erweiterung am Anfang schneidet.
Außerdem werden die Erweiterungen in Kleinbuchstaben konvertiert.

Beispielausgabe:

   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
quelle
auf dem Mac, uniqhat nicht die volle Flagge --count, -cfunktioniert aber
einwandfrei
12

Finde alles mit einem Punkt und zeige nur das Suffix.

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

Wenn Sie wissen, dass alle Suffixe 3 Zeichen haben, dann

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

oder mit sed zeigt alle Suffixe mit ein bis vier Zeichen. Ändern Sie {1,4} in den Zeichenbereich, den Sie im Suffix erwarten.

find . -type f | sed -n 's/.*\.\(.\{1,4\}\)$/\1/p'| sort -u
user224243
quelle
1
Das Rohr muss nicht sortiert werden, awk kann alles: finden. -typ f -name " . " | awk -F. '! a [$ NF] ++ {print $ NF}'
SiegeX
@SiegeX Ihre Antwort sollte separat sein. Dieser Befehl funktioniert am besten für große Ordner, da die Erweiterungen so gedruckt werden, wie sie gefunden werden. Aber beachten Sie, dass es sein sollte: -name " . "
Ralf
@Ralf fertig, Antwort hier gepostet . Ich -name "."
bin
Ich meinte, es sollte -name "*. *" Sein, aber StackOverflow entfernt die * Zeichen, was wahrscheinlich auch in Ihrem Kommentar passiert ist.
Ralf
Es scheint, dass dies die akzeptierte Antwort sein sollte. Awk ist Perl als Befehlszeilentool vorzuziehen und umfasst die Unix-Philosophie, kleine interoperable Programme in zusammenhängende und lesbare Prozeduren zu leiten.
Jon z
7

Hinzufügen meiner eigenen Variation zur Mischung. Ich denke, es ist das einfachste von allen und kann nützlich sein, wenn Effizienz kein großes Problem ist.

find . -type f | grep -o -E '\.[^\.]+$' | sort -u
gkb0986
quelle
1
+1 für Portabilität, obwohl der reguläre Ausdruck ziemlich begrenzt ist, da er nur Erweiterungen entspricht, die aus einem einzelnen Buchstaben bestehen. Die Verwendung des $ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
regulären Ausdrucks
1
Einverstanden. Ich habe dort etwas nachgelassen. Bearbeiten meiner Antwort, um den Fehler zu beheben, den Sie entdeckt haben.
gkb0986
cool. Ich chenge Anführungszeichen zu Doppelzitaten, aktualisiere grep- Biraries und Abhängigkeiten (weil mit git versehen veraltet ist) und jetzt funktioniert dies unter Windows. fühle mich wie ein Linux-Benutzer.
Msangel
5

Verwenden Sie in Python Generatoren für sehr große Verzeichnisse, einschließlich leerer Erweiterungen, und ermitteln Sie, wie oft jede Erweiterung angezeigt wird:

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
quelle
5

Ich habe hier eine Reihe von Antworten ausprobiert, sogar die "beste" Antwort. Sie alle kamen zu kurz, was ich speziell wollte. Abgesehen von den letzten 12 Stunden, in denen ich für mehrere Programme im Regex-Code gesessen und diese Antworten gelesen und getestet habe, habe ich mir genau das ausgedacht, was genau so funktioniert, wie ich es möchte.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Findet alle Dateien, die möglicherweise eine Erweiterung haben.
  • Greift nur die Erweiterung
  • Greift nach Dateierweiterungen zwischen 2 und 16 Zeichen (passen Sie die Zahlen einfach an, wenn sie nicht Ihren Anforderungen entsprechen). Dies hilft, Cache-Dateien und Systemdateien zu vermeiden (Systemdateibit dient zum Durchsuchen des Gefängnisses).
  • Awk, um die Erweiterungen in Kleinbuchstaben zu drucken.
  • Sortieren Sie und bringen Sie nur eindeutige Werte ein. Ursprünglich hatte ich versucht, die awk-Antwort zu versuchen, aber es wurden Elemente doppelt gedruckt, die sich in der Groß- und Kleinschreibung unterschieden.

Wenn Sie eine Anzahl der Dateierweiterungen benötigen, verwenden Sie den folgenden Code

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

Obwohl diese Methoden einige Zeit in Anspruch nehmen und wahrscheinlich nicht die besten Methoden sind, um das Problem zu lösen, funktionieren sie.

Update: Per @ alpha_989 verursachen lange Dateierweiterungen ein Problem. Das liegt an der ursprünglichen Regex "[[: alpha:]] {3,6}". Ich habe die Antwort aktualisiert und den regulären Ausdruck "[[: alpha:]] {2,16}" eingefügt. Jeder, der diesen Code verwendet, sollte sich jedoch darüber im Klaren sein, dass diese Zahlen die Mindest- und Höchstwerte für die Dauer der Erweiterung für die endgültige Ausgabe sind. Alles, was außerhalb dieses Bereichs liegt, wird in der Ausgabe in mehrere Zeilen aufgeteilt.

Hinweis: Der ursprüngliche Beitrag lautete "- Greift nach Dateierweiterungen zwischen 3 und 6 Zeichen (passen Sie die Zahlen einfach an, wenn sie nicht Ihren Anforderungen entsprechen). Dies hilft, Cache-Dateien und Systemdateien zu vermeiden (Systemdateibit dient zum Durchsuchen des Gefängnisses). ""

Idee: Kann verwendet werden, um Dateierweiterungen über eine bestimmte Länge zu finden über:

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

Wobei 4 die Länge der einzuschließenden Dateierweiterungen ist und dann auch alle Erweiterungen über diese Länge hinaus gefunden werden.

Shinrai
quelle
Ist die Zählversion rekursiv?
Fernando Montoya
@Shinrai, funktioniert im Allgemeinen gut. Wenn Sie jedoch einige zufällige Dateierweiterungen haben, die wirklich lang sind, wie z. B. .download, wird das ".download" in zwei Teile aufgeteilt und 2 Dateien gemeldet, eine mit "downlo" und eine mit "ad"
alpha_989
@ alpha_989, Das liegt daran, dass der reguläre Ausdruck "[[: alpha:]] {3,6}" auch ein Problem mit Erweiterungen verursacht, die kleiner als 3 Zeichen sind. Passen Sie an, was Sie brauchen. Persönlich würde ich sagen, dass 2,16 in den meisten Fällen funktionieren sollte.
Shinrai
Danke, dass du geantwortet hast. Ja, das habe ich später gemerkt. Es hat gut funktioniert, nachdem ich es ähnlich wie das, was Sie erwähnt haben, modifiziert habe.
Alpha_989
3

Da es bereits eine andere Lösung gibt, die Perl verwendet:

Wenn Sie Python installiert haben, können Sie auch (über die Shell) Folgendes tun:

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
quelle
2

Keine der Antworten befasst sich bisher ordnungsgemäß mit Dateinamen mit Zeilenumbrüchen (mit Ausnahme von ChristopheDs, die gerade eingingen, als ich dies tippte). Das Folgende ist kein Shell-Einzeiler, funktioniert aber und ist relativ schnell.

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

quelle
2

Ich glaube, dieser wurde noch nicht erwähnt:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c
Dmitry B.
quelle
Dies wäre wahrscheinlich ziemlich langsam, da für jede Datei ein neuer Prozess erzeugt wird.
Ondra Žižka
1

Ich denke, der einfachste und direkteste Weg ist

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

Es wurde auf ChristopheDs 3. Weg modifiziert.

Robert
quelle
0

Sie könnten dies auch tun

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

Ich habe es einfach und schnell gefunden ...

   # 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
quelle
0

Die akzeptierte Antwort verwendet REGEX und Sie können mit REGEX keinen Alias-Befehl erstellen. Sie müssen ihn in ein Shell-Skript einfügen. Ich verwende Amazon Linux 2 und habe Folgendes ausgeführt:

  1. Ich habe den akzeptierten Antwortcode in eine Datei eingefügt, indem ich:

    sudo vim find.sh

Fügen Sie diesen Code hinzu:

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

Speichern Sie die Datei, indem Sie Folgendes eingeben: :wq!

  1. sudo vim ~/.bash_profile

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

  3. :wq!

  4. . ~/.bash_profile

Chris Medina
quelle