POSIX-Suche auf bestimmte Tiefe beschränken?

15

Ich habe kürzlich festgestellt, dass die POSIX-Spezifikationen fürfind nicht die -maxdepthPrimärdaten enthalten.

Für diejenigen, die nicht damit vertraut sind, besteht der Zweck des -maxdepthprimären darin, zu begrenzen, wie viele Ebenen tief findabgesenkt werden. -maxdepth 0führt dazu, dass nur Befehlszeilenargumente verarbeitet werden. -maxdepth 1würde nur Ergebnisse direkt in den Kommandozeilenargumenten behandeln, etc.

Wie kann ich -maxdepthmit nur von POSIX angegebenen Optionen und Tools das dem Nicht-POSIX- Primärsystem entsprechende Verhalten erzielen?

(Hinweis: Natürlich kann ich das Äquivalent dazu erhalten, -maxdepth 0indem ich nur -pruneden ersten Operanden verwende, aber das erstreckt sich nicht auf andere Tiefen.)

Platzhalter
quelle
@StevenPenny, FreeBSD's -depth -2, -depth 1... Ansatz könnte als besser angesehen werden als GNU's -maxdepth/-mindepth
Stéphane Chazelas
@ StéphaneChazelas so oder so - POSIX find sollte das eine oder andere haben; sonst ist es verkrüppelt
Steven Penny
1
Zumindest für -maxdepth/ -mindepthgibt es sinnvolle Alternativen (beachten Sie, dass -pathes sich um eine neue Ergänzung zu POSIX handelt). Die Alternativen für -timexyoder -mtime -3m(oder -mmin -3) sind viel umständlicher. Manche mögen -execdir/ -deletehaben keine verlässliche Alternative.
Stéphane Chazelas
2
@StevenPenny, zögern Sie nicht, ein Ticket bei austingroupbugs.net zu registrieren , um es hinzuzufügen. Ich habe gesehen, dass Dinge hinzugefügt wurden, ohne dass ein Sponsor erforderlich war, als es eine starke Rechtfertigung gab. Eine wahrscheinlich bessere Vorgehensweise wäre, wenn möglichst viele Implementierungen zuerst hinzugefügt würden, sodass POSIX nur das vorhandene angeben müsste, was im Allgemeinen weniger umstritten ist.
Stéphane Chazelas
@ StéphaneChazelas in meinem Fall habe ich die Dateien nur direkt benannt, aber danke; Ich könnte ein Ticket einreichen, wenn dies wieder auftaucht
Steven Penny

Antworten:

7

Sie können verwenden -path, um eine bestimmte Tiefe abzugleichen und dort zu beschneiden. Z.B

find . -path '*/*/*' -prune -o -type d -print

würde maxdepth 1 sein, da *Übereinstimmungen mit ., */*Übereinstimmungen ./dir1und */*/*Übereinstimmungen, ./dir1/dir2die beschnitten werden. Wenn Sie ein absolutes Startverzeichnis verwenden müssen Sie einen führende hinzufügen /zu dem -pathzu.

meuh
quelle
Hmmm, knifflig. Könnten Sie nicht einfach eine Schicht /*vom Ende des Musters entfernen , den -oOperator entfernen und das gleiche Ergebnis erzielen?
Wildcard
Nein, weil auch *Streichhölzer /, also würde das Dir leider a/b/c/d/epassen -path */*.
Meuh
Aber a/b/c/d/ewürde niemals erreicht , da -prunewürde angewandt werden a/b....
Wildcard
1
Entschuldigung, ich habe das falsch verstanden -pruneund -owurde entfernt. Wenn Sie das beibehalten, besteht -prunedas Problem darin, dass */*nichts auf einer Ebene oberhalb der maximalen Tiefe übereinstimmt, z a. B. das einzelne Verzeichnis .
Meuh
11

@ meuhs Ansatz ist ineffizient, da er -maxdepth 1weiterhin findden Inhalt von Verzeichnissen auf Ebene 1 liest, um sie später zu ignorieren. Bei einigen findImplementierungen (einschließlich GNU find) funktioniert dies auch nicht ordnungsgemäß, wenn einige Verzeichnisnamen Folgen von Bytes enthalten, die im Gebietsschema des Benutzers keine gültigen Zeichen bilden (wie bei Dateinamen mit einer anderen Zeichenkodierung).

find . \( -name . -o -prune \) -extra-conditions-and-actions

ist der kanonischere Weg, GNUs -maxdepth 1(oder FreeBSDs -depth -2) zu implementieren .

Im Allgemeinen ist es jedoch -depth 1so, dass Sie es wollen ( -mindepth 1 -maxdepth 1), wie Sie es nicht wollen .(Tiefe 0), und dann ist es noch einfacher:

find . ! -name . -prune -extra-conditions-and-actions

Denn -maxdepth 2das wird:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Und hier stoßen Sie auf die Probleme mit ungültigen Charakteren.

Wenn Sie beispielsweise ein Verzeichnis mit dem Namen haben Stéphane, das éjedoch im Zeichensatz iso8859-1 (auch bekannt als latin1) (0xe9-Byte) codiert ist, wie es in Westeuropa und den USA bis Mitte der 2000er Jahre am häufigsten vorkam, dann ist dieses 0xe9-Byte kein gültiges Zeichen in UTF-8. Also, in UTF-8 - Sprachumgebungen, die *Platzhalter (mit einigen findImplementierungen) nicht übereinstimmen Stéphaneals *0 oder mehr Zeichen und 0xE9 ist kein Charakter.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

My find(wenn die Ausgabe an ein Terminal geht) zeigt dieses ungültige 0xe9-Byte wie ?oben an. Sie können sehen, das St<0xe9>phane/Chazelaswar nicht pruned.

Sie können dies umgehen, indem Sie Folgendes tun:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Beachten Sie jedoch, dass dies Auswirkungen auf alle Gebietsschemaeinstellungen findund auf alle ausgeführten Anwendungen hat (z. B. über die -execPrädikate).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Jetzt bekomme ich wirklich eine -maxdepth 2aber beachten Sie, wie die in UTF-8 richtig codierten é in der zweiten Stéphane ??als die 0xc3 0xa9-Bytes (betrachtet als zwei einzelne undefinierte Zeichen in der C-Ländereinstellung) der UTF-8-Codierung von é angezeigt werden Nicht druckbare Zeichen im Gebietsschema C.

Und wenn ich ein hinzugefügt hätte -name '????????', hätte ich die falsche Stéphane (die in ISO8859-1 codierte) bekommen.

Um sie auf beliebige Pfade anzuwenden ., gehen Sie wie folgt vor:

find some/dir/. ! -name . -prune ...

für -mindepth 1 -maxdepth 1oder:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

für -maxdepth 2.

Ich würde immer noch Folgendes tun:

(cd -P -- "$dir" && find . ...)

Erstens, weil dadurch die Pfade kürzer werden, wodurch es weniger wahrscheinlich ist, dass sie auf einen zu langen Pfad oder eine zu lange Liste vonfind Argumenten stoßen, sondern auch die Tatsache umgangen wird , dass willkürliche Pfadargumente (außer -fmit FreeBSD find) nicht unterstützt werden, da sie ersticken Werte $dirwie !oder -print...


Die -oKombination mit Negation ist ein häufiger Trick, um zwei unabhängige Sätze von -condition/ -actionin auszuführen find.

Wenn Sie in -action1Dateikonferenzen -condition1und unabhängig -action2in Dateikonferenzen ausführen möchten -condition2, können Sie Folgendes nicht ausführen :

find . -condition1 -action1 -condition2 -action2

As -action2würde nur für Dateien ausgeführt, die beide Bedingungen erfüllen .

Noch:

find . -contition1 -action1 -o -condition2 -action2

Wie -action2würde für Dateien nicht ausgeführt werden, der treffen beide Bedingungen.

find . \( ! -condition1 -o -action1 \) -condition2 -action2

funktioniert so \( ! -condition1 -o -action1 \), wie es für jede Datei auf true aufgelöst würde . Dies setzt voraus , dass -action1eine Aktion (wie ) immer true zurückgibt . Für Aktionen wie diese , die möglicherweise false zurückgeben , möchten Sie möglicherweise eine andere hinzufügen, bei der harmlos ist, die jedoch true zurückgibt , wie in GNU oder oder (beachten Sie jedoch das Problem mit ungültigen Zeichen oben).-prune-exec ... {} +-exec ... \;-o -something-something-truefind-links +0-name '*'

Stéphane Chazelas
quelle
1
Eines Tages werde ich auf ein paar chinesische Dateien stoßen und ich bin sehr froh, dass ich Ihre vielen Antworten zu Gebietsschema und gültigen Zeichen gelesen habe. :)
Wildcard
2
@Wildcard, es ist wahrscheinlicher, dass Sie (und vor allem ein Chinese) Probleme mit britischen, französischen ... Dateinamen haben als mit chinesischen Dateinamen, da chinesische Dateinamen in UTF-8 häufiger verschlüsselt werden als Dateinamen von alphabetischen Skripten Dies kann im Allgemeinen durch einen Einzelbyte-Zeichensatz abgedeckt werden, der bis vor kurzem die Norm war. Es gibt andere Multi-Byte-Zeichensätze für chinesische Schriftzeichen, aber ich würde erwarten, dass Chinesen früher auf UTF-8 umgestiegen sind als Westler, da diese Zeichensätze eine Reihe von unangenehmen Problemen aufweisen. Siehe auch die Bearbeitung für ein Beispiel.
Stéphane Chazelas
0

Ich stieß auf ein Problem, bei dem ich beim Durchsuchen mehrerer Pfade (statt nur .) die Tiefe begrenzen musste .

Beispielsweise:

$ find dir1 dir2 -name myfile -maxdepth 1

Dies führte mich zu einem alternativen Ansatz mit -regex. Das Wesentliche ist:

-regex '(<list of paths | delimited>)/<filename>'

Das obige wäre also:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

Ohne einen Dateinamen:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

Schließlich -maxdepth 2ändert sich der reguläre Ausdruck zu:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'

Alissa H
quelle
1
In dieser Frage wird jedoch nach einer Standardlösung (wie in POSIX) gefragt. Auch -maxdepthwürde arbeiten mit mehreren Suchpfaden.
Kusalananda