it-swarm.com.de

Ruft eine Liste der Unterverzeichnisse ab, die eine Datei enthalten, deren Name eine Zeichenfolge enthält

Wie kann ich eine Liste der Unterverzeichnisse abrufen, die eine Datei enthalten, deren Name einem bestimmten Muster entspricht?

Insbesondere suche ich nach Verzeichnissen, die eine Datei mit dem Buchstaben 'f' enthalten, die irgendwo im Dateinamen vorkommt.

Im Idealfall enthält die Liste keine Duplikate und enthält nur den Pfad ohne den Dateinamen.

47
Muhd
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq

Das Obige findet alle Dateien unter dem aktuellen Verzeichnis (.), Die reguläre Dateien (-type f) Sind und f irgendwo in ihrem Namen haben (-name '*f*'). Als nächstes entfernt sed den Dateinamen und lässt nur den Verzeichnisnamen übrig. Anschließend wird die Liste der Verzeichnisse sortiert (sort) und Duplikate entfernt (uniq).

Der Befehl sed besteht aus einem einzelnen Ersatz. Es sucht nach Übereinstimmungen mit dem regulären Ausdruck /[^/]+$ Und ersetzt alles, was dazu passt, durch nichts. Das Dollarzeichen bedeutet das Ende der Zeile. [^/]+' Bedeutet ein oder mehrere Zeichen, die keine Schrägstriche sind. Somit bedeutet /[^/]+$ Alle Zeichen vom letzten Schrägstrich bis zum Ende der Zeile. Mit anderen Worten, dies entspricht dem Dateinamen am Ende des vollständigen Pfads. Daher entfernt der Befehl sed den Dateinamen und lässt den Namen des Verzeichnisses, in dem sich die Datei befand, unverändert.

Vereinfachungen

Viele moderne sort -Befehle unterstützen ein -u - Flag, wodurch uniq unnötig wird. Für GNU sed:

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u

Und für MacOS sed:

find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u

Wenn Ihr Befehl find dies unterstützt, ist es auch möglich, dass find die Verzeichnisnamen direkt druckt. Dies vermeidet die Notwendigkeit von sed:

find . -type f -name '*f*' -printf '%h\n' | sort -u

Robustere Version (Benötigt GNU Tools)

Die obigen Versionen werden durch Dateinamen verwechselt, die Zeilenumbrüche enthalten. Eine robustere Lösung besteht darin, die Sortierung nach NUL-terminierten Zeichenfolgen durchzuführen:

find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
45
John1024

Warum versuchen Sie das nicht:

find / -name '*f*' -printf "%h\n" | sort -u
26
Patrick Taylor

Es gibt im Wesentlichen zwei Methoden, mit denen Sie dies tun können. Einer analysiert die Zeichenfolge, während der andere jede Datei bearbeitet. Das Parsen der Zeichenfolge mit einem Tool wie grep, sed oder awk wird natürlich schneller sein, aber hier ist ein Beispiel, das sowohl das Profil als auch das Profil zeigt "die 2 Methoden.

Beispieldaten

Für die folgenden Beispiele verwenden wir die folgenden Daten

$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}

Löschen Sie einige der *f* Dateien von dir1/*:

$ rm dir1/dir10{0..2}/*f*

Ansatz 1 - Parsen über Strings

Hier verwenden wir die folgenden Tools: find, grep und sort.

$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/

Ansatz 2 - Parsen mit Dateien

Gleiche Werkzeugkette wie zuvor, außer dass wir dieses Mal dirname anstelle von grep verwenden.

$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107

HINWEIS : Die obigen Beispiele verwenden head -5, um lediglich die Ausgabemenge zu begrenzen, mit der wir uns für diese Beispiele befassen. Sie werden normalerweise entfernt, um Ihre vollständige Liste zu erhalten!

Vergleich der Ergebnisse

Wir können time verwenden, um einen Blick auf die beiden Ansätze zu werfen.

dirname

real        0m0.372s
user        0m0.028s
sys         0m0.106s

grep

real        0m0.012s
user        0m0.009s
sys         0m0.007s

Es ist also immer am besten, wenn möglich mit den Saiten umzugehen.

Alternative Methoden zum Parsen von Zeichenfolgen

grep & PCRE

$ find . -type f -name '*f*' | grep  -oP '^.*(?=/)' | sort -u

sed

$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u

awk

$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u
8
slm

Hier ist eine, die ich nützlich finde:

find . -type f -name "*somefile*" | xargs dirname | sort | uniq
3
Martin Tapp

Diese Antwort basiert schamlos auf der Antwort von slm. Es war ein interessanter Ansatz, aber es gibt eine Einschränkung, wenn die Datei- und/oder Verzeichnisnamen spezielle Zeichen hatten (Leerzeichen, Halbspalte ...). Eine gute Angewohnheit ist es, find /somewhere -print0 | xargs -0 someprogam.

Beispieldaten

Für die folgenden Beispiele verwenden wir die folgenden Daten

mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}

Löschen Sie einige der *f* Dateien von dir1/*/:

rm dir1/dir\ 10{0..2}/*f*

Ansatz 1 - Parsen mit Dateien

$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107

NOTE : Die obigen Beispiele verwenden head -5, um lediglich die Ausgabemenge zu begrenzen, mit der wir uns für diese Beispiele befassen. Sie werden normalerweise entfernt, um Ihre vollständige Liste zu erhalten! Ersetzen Sie außerdem das echo, welches den gewünschten Befehl verwenden soll.

1
Franklin Piat

Mit zsh:

typeset -aU dirs # array with unique values
dirs=(**/*f*(D:h))

printf '%s\n' $dirs
1