find: prune ignoriert den angegebenen Pfad nicht

11

Ich muss .gitvon meiner findSuche ausschließen. Um dies zu erreichen, benutze ich den -path ./.git -pruneSchalter:

$ find . -path ./.git -prune -o \( -type f -o -type l -o -type d \) | grep '.git'
./.git

Obwohl dies den Inhalt des .git-Verzeichnisses überspringt, wird das Verzeichnis selbst aufgelistet. Es funktioniert, wenn ich hinzufüge-path ./.git -prune -o -print -a

find . -path ./.git -prune -o -print -a \( -type f -o -type l -o -type d \) | grep '.git'

Warum ist das notwendig? Ich dachte, dass beide Befehle die gleiche Ausgabe haben sollten. Die zweite Syntax ist ziemlich hässlich.

Martin Vegter
quelle
4
Sie brauchen eine -print, sonst gilt das Implizite -printfür die gesamte Bedingung
Stéphane Chazelas
1
Siehe auch unix.stackexchange.com/q/102191/22565
Stéphane Chazelas

Antworten:

3

Die manSeite für findgibt:

-prune True; if the file is a directory, do not  descend  into  it.  If
      -depth  is  given,  false;  no  effect.  Because -delete implies
      -depth, you cannot usefully use -prune and -delete together.

Im ersten Beispiel ist dies nicht der Fall, -path ./.git -pruneund daher wird die Standardaktion ( -print) nicht aufgerufen. Daher wird die Zeile gedruckt.

Timo
quelle
22

Ich war verwirrt darüber, warum der findBefehl auch beschnittene Verzeichnisse druckte , und einige andere komplizierte Details darüber -prune, wie es funktionierte, konnte es aber anhand einiger Beispiele herausfinden.

Um die folgenden Beispiele auszuführen, erstellen Sie die folgenden Verzeichnisse und Dateien.

mkdir aa
mkdir bb
touch file1
touch aa/file1
touch bb/file3

So erstellen Sie diese Struktur:

$ tree

.
├── aa
   └── file1
├── bb
   └── file3
└── file1

Verwenden Sie nun find, um nach benannten Verzeichnissen zu suchen aa. Kein Problem hier.

$ find . -type d -name aa
./aa

Suchen Sie nach allen Verzeichnissen außer aa und wir erhalten das aktuelle Verzeichnis .und ./bb, was auch Sinn macht.

$ find . -type d ! -name aa
.
./bb

So weit so gut, aber wenn wir verwenden -prune, gibt find das Verzeichnis zurück, das wir bereinigen, was mich anfangs verwirrte, weil ich erwartet hatte, dass es alle anderen Verzeichnisse zurückgibt und nicht dasjenige, das beschnitten wird.

$ find . -type d -name aa -prune
./aa

Der Grund, warum das zu bereinigende Verzeichnis zurückgegeben wird, wird nicht im -pruneAbschnitt der Manpages erläutert, wie in Timos Antwort angegeben , sondern im EXPRESSIONSAbschnitt:

Wenn der Ausdruck keine anderen Aktionen als enthält -prune,  -printwird er für alle Dateien ausgeführt, für die der Ausdruck wahr ist.

Dies bedeutet, dass der Ausdruck, da er mit dem aaVerzeichnisnamen übereinstimmt , als wahr ausgewertet und gedruckt wird, da find implizit ein -printam Ende des gesamten Befehls hinzufügt . Es wird jedoch kein hinzugefügt -print, wenn Sie die Aktion absichtlich -o -printselbst zum Ende hinzufügen :

find . -type d -name aa -prune -o -print
.
./file1
./bb
./bb/file3

Hier fügt der Befehl find KEIN Implizites -printmehr hinzu, sodass das Verzeichnis, das wir beschneiden ( aa), nicht gedruckt wird.

Wenn wir also eine Klausel hinzufügen, die nach Dateien mit einem Dateinamenmuster file*nach dem sucht -o, müssen Sie -printam Ende dieser zweiten Klausel eine folgende setzen:

find . \( -type d -name aa -prune \) -o \( -type f -name 'file*' -print \)
./file1
./bb/file3

Der Grund, warum dies funktioniert, ist der gleiche: Wenn Sie -printin der zweiten Klausel keine -pruneeinfügen, fügt find -printam Ende des Befehls automatisch eine hinzu , da die Aktion keine andere als die Aktion enthält , wodurch die -pruneKlausel die druckt beschnittenes Verzeichnis:

find . \( \( -type d -name aa -prune \) -o \( -type f -name 'file*' \) \) -print
./aa
./file1
./bb/file3

Im Allgemeinen müssen Sie den -printBefehl in die zweite Klausel einfügen. Wenn Sie es wie das Originalplakat in der Mitte platzieren, funktioniert es nicht richtig, da die zu beschneidenden Dateien sofort gedruckt werden und die zweite Klausel keine Chance hat, die gewünschten Dateien auszuwählen:

find . \( -type d -name aa -prune -o -print \) -o \( -type f -name 'file*' \)
.
./file1
./bb
./bb/file3

Leider hat das Originalplakat den obigen Befehl falsch platziert, indem es -printan der falschen Stelle platziert wurde. Es könnte für seinen speziellen Fall funktioniert haben, aber es funktioniert im allgemeinen Fall nicht.

Es gibt Tausende von Menschen, die Schwierigkeiten haben zu verstehen, wie es -prunefunktioniert. Die findManpage sollte aktualisiert werden, um die unendliche weltweite Verwirrung über diesen Befehl zu vermeiden.

Jerry Marbas
quelle
In meiner Manpage (Arch Linux find-4.6.0) heißt es: "Wenn der gesamte Ausdruck keine anderen Aktionen als -prune oder -print enthält, wird -print für alle Dateien ausgeführt, für die der gesamte Ausdruck wahr ist." Funktioniert aber so wie du es beschreibst.
X-Yuri
0

von manSeite,

Verwenden Sie -prune, um einen gesamten Verzeichnisbaum zu ignorieren, anstatt jede Datei im Baum zu überprüfen. Um beispielsweise das Verzeichnis "src / emacs" und alle Dateien und Verzeichnisse darunter zu überspringen und die Namen der anderen gefundenen Dateien auszudrucken, gehen Sie wie folgt vor:

find . -path ./src/emacs -prune -o -print

Vielleicht können Sie Folgendes verwenden:

find . -path ./.git -prune -o -print

Dadurch wird das .gitVerzeichnis nicht aufgelistet .

Um einen Dateinamen festzulegen, können Sie Folgendes tun:

find . -path ./.git -prune , -name filename

Bitte beachten Sie den ,Komma-Operator.

jianyongli
quelle
-1

Hier ist eine schnelle und schmutzige Alternative:

find | fgrep -v /.git

Ich kann mich einfach nicht an finddie komplexe Syntax erinnern ...

peter.slizik
quelle