Wie verwende ich die Option '-prune' von 'find' in sh?

219

Ich verstehe das Beispiel aus dem nicht ganz. man findKann mir jemand einige Beispiele und Erklärungen geben? Kann ich reguläre Ausdrücke darin kombinieren?


Die detailliertere Frage lautet wie folgt:

Schreiben Sie ein Shell-Skript, changealldas eine Schnittstelle wie hat changeall [-r|-R] "string1" "string2". Es werden alle Dateien mit einem Suffix finden .h, .C, .cc, oder .cppund alle Vorkommen ändern string1zu string2. -rist die Option, nur im aktuellen Verzeichnis zu bleiben oder Unterverzeichnisse einzuschließen.

HINWEIS:

  1. Für nicht rekursive Fälle lsist NICHT erlaubt, wir könnten nur findund verwenden sed.
  2. Ich habe es versucht, find -depthaber es wurde NICHT unterstützt. Deshalb habe ich mich gefragt, ob ich -prunehelfen könnte, aber das Beispiel von nicht verstanden man find.

EDIT2: Ich habe einen Auftrag ausgeführt, ich habe keine detaillierten Fragen gestellt, weil ich ihn gerne selbst beenden würde. Da ich es bereits getan und abgegeben habe, kann ich jetzt die ganze Frage stellen. Außerdem habe ich es geschafft, die Aufgabe ohne Verwendung zu beenden -prune, möchte sie aber trotzdem lernen.

derrdji
quelle

Antworten:

438

Das, was ich verwirrend fand, -pruneist, dass es eine Aktion (wie -print) ist, kein Test (wie -name). Es ändert die "To-Do" -Liste, gibt aber immer true zurück .

Das allgemeine Verwendungsmuster -prunelautet wie folgt:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

Sie möchten so ziemlich immer das -o(logische ODER) unmittelbar danach -prune, da dieser erste Teil des Tests (bis einschließlich -prune) für das tatsächlich gewünschte Material (dh das Material, das Sie nicht entfernen möchten ) false zurückgibt.

Hier ist ein Beispiel:

find . -name .snapshot -prune -o -name '*.foo' -print

Dadurch werden die "* .foo" -Dateien gefunden, die sich nicht in den Verzeichnissen ".snapshot" befinden. In diesem Beispiel -name .snapshotbildet das [conditions to prune], und -name '*.foo' -printist [your usual conditions]und [actions to perform].

Wichtige Hinweise :

  1. Wenn Sie nur die Ergebnisse drucken möchten, sind Sie möglicherweise daran gewöhnt, die -printAktion auszulassen. Sie möchten dies im Allgemeinen nicht tun, wenn Sie verwenden -prune.

    Das Standardverhalten von find ist "und" des gesamten Ausdrucks mit der -printAktion, wenn -pruneam Ende keine anderen Aktionen als (ironischerweise) vorhanden sind . Das bedeutet, dass dies geschrieben wird:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    ist gleichbedeutend mit dem Schreiben von:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    Dies bedeutet, dass auch der Name des Verzeichnisses ausgedruckt wird, das Sie normalerweise entfernen. Dies ist normalerweise nicht das, was Sie möchten. Stattdessen ist es besser, die -printAktion explizit anzugeben, wenn Sie dies möchten:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Wenn Ihre "normale Bedingung" mit Dateien übereinstimmt, die auch Ihrer Bereinigungsbedingung entsprechen, werden diese Dateien nicht in die Ausgabe aufgenommen. Die Möglichkeit, dies zu beheben, besteht darin -type d, Ihrem Bereinigungszustand ein Prädikat hinzuzufügen .

    Nehmen wir zum Beispiel an, wir wollten jedes Verzeichnis .gitentfernen, mit dem begonnen wurde (dies ist zugegebenermaßen etwas erfunden - normalerweise müssen Sie nur das genau genannte Objekt entfernen .git), aber ansonsten wollten wir alle Dateien sehen, einschließlich Dateien wie .gitignore. Sie könnten dies versuchen:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    Dies würde nicht enthalten .gitignorein der Ausgabe. Hier ist die feste Version:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

Zusätzlicher Tipp: Wenn Sie die GNU-Version von verwenden find, enthält die texinfo-Seite für findeine ausführlichere Erklärung als die Manpage (wie dies für die meisten GNU-Dienstprogramme gilt).

Laurence Gonsalves
quelle
6
Es ist nicht 100% offensichtlich in Ihrem Text (aber da Sie nur '* .foo' drucken, kommt es nicht zu Konflikten), aber der -prune-Teil druckt auch nichts (nicht nur Verzeichnisse) mit dem Namen ".snapshot". Das heißt, es -prunefunktioniert nicht nur für Verzeichnisse (sondern verhindert bei Verzeichnissen auch die Eingabe von Verzeichnissen, die dieser Bedingung entsprechen, dh hier die Verzeichnisse, die dieser Bedingung entsprechen -name .snapshot).
Olivier Dulac
12
und +1 für Sie für die gut gemachte Erklärung (und besonders den wichtigen Hinweis). Sie sollten dies den Find-Entwicklern vorlegen (da die Manpage "Beschneiden" für normale Menschen nicht erklärt ^^ Ich habe viele Versuche gebraucht, um es herauszufinden, und ich habe diesen Nebeneffekt, vor dem Sie uns warnen, nicht gesehen).
Olivier Dulac
2
@OlivierDulac Das ist ein sehr guter Punkt, um möglicherweise Dateien zu entfernen, die Sie behalten möchten. Ich habe die Antwort aktualisiert, um dies zu verdeutlichen. es ist -pruneübrigens nicht selbst, was dies verursacht. Das Problem ist, dass der oder Operator "kurzschließt" und / oder eine niedrigere Priorität als und hat. Das Endergebnis ist, dass eine aufgerufene Datei .snapshotmit der ersten übereinstimmt -name, -prunedann nichts tut (aber true zurückgibt) und dann oder zurückgibt, da das linke Argument true war. Die Aktion (zB :) -printist Teil ihres zweiten Arguments, sodass sie niemals ausgeführt werden kann.
Laurence Gonsalves
3
+1 endlich herausgefunden, warum ich -printam Ende brauche , kann ich jetzt aufhören, \! -path <pattern>zusätzlich zu-prune
Miserable Variable
6
Beachten Sie, dass "-o" eine Abkürzung für "-or" ist, die (obwohl nicht POSIX-kompatibel) deutlicher gelesen wird.
Jojo
27

Normalerweise ist die native Art, wie wir Dinge unter Linux tun und wie wir denken, von links nach rechts.
Sie würden also zuerst schreiben, wonach Sie suchen:

find / -name "*.php"

Dann drücken Sie wahrscheinlich die Eingabetaste und stellen fest, dass Sie zu viele Dateien aus Verzeichnissen erhalten, die Sie nicht möchten. Lassen Sie uns / media ausschließen, um zu vermeiden, dass Ihre gemounteten Laufwerke durchsucht werden.
Sie sollten jetzt nur Folgendes an den vorherigen Befehl anhängen:

-print -o -path '/media' -prune

Der letzte Befehl lautet also:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- Include ---> | .................... | <- -------- Ausschließen ---------> |

Ich denke, diese Struktur ist viel einfacher und korreliert mit dem richtigen Ansatz

AmitP
quelle
3
Ich hätte nicht erwartet, dass dies effizient ist - ich hätte gedacht, dass die linke Klausel zuerst vor dem Beschneiden bewertet wird, aber zu meiner Überraschung scheint ein schneller Test darauf hinzudeuten, dass dies findklug genug ist, um die -pruneKlausel zuerst zu verarbeiten. Hmmm, interessant.
Artfulrobot
Ich hatte nie gedacht, dass in fast einem Jahrzehnt der Verwendung von GNU zu finden! Danke für das! Es wird definitiv die Art und Weise ändern, wie ich -prunevon nun an darüber nachdenke .
Felipe Alvarez
3
@artfulrobot Verarbeitet es es wirklich zuerst? Ich hätte gedacht /media, dass es eintritt , bemerkt, dass es nicht aufgerufen wird, *.phpund dann überprüft, ob es sich gerade im Inneren /mediabefindet, es gesehen hat und daher den gesamten Teilbaum übersprungen hat. Es ist immer noch von links nach rechts, es macht nur keinen Unterschied, solange sich beide Prüfungen nicht überschneiden.
Phk
26

Beachten Sie, dass -prune nicht verhindert, dass Sie in ein Verzeichnis absteigen, wie einige gesagt haben. Es verhindert das Absteigen in Verzeichnisse, die dem Test entsprechen, auf den es angewendet wird. Vielleicht helfen einige Beispiele (siehe unten für ein Regex-Beispiel). Tut mir leid, dass das so lang ist.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2
Bis auf weiteres angehalten.
quelle
Wenn Sie auch "./dir1/scripts/test" berühren (dh eine "Test" -Datei und nicht dir in diesem ausgedruckten Unterverzeichnis haben), wird es nicht gedruckt durch find . -name test -prune -o -print: iow, -pruneist eine Aktion, die auch arbeitet an Dateien
Olivier Dulac
10

Hinzufügen zu den Ratschlägen in anderen Antworten (ich habe keinen Repräsentanten, um Antworten zu erstellen) ...

Bei der Kombination -prunemit anderen Ausdrücken gibt es einen subtilen Unterschied im Verhalten, je nachdem, welche anderen Ausdrücke verwendet werden.

Das Beispiel von @Laurence Gonsalves findet die "* .foo" -Dateien, die sich nicht in den Verzeichnissen ".snapshot" befinden: -

find . -name .snapshot -prune -o -name '*.foo' -print

Diese etwas andere Kurzschrift listet jedoch möglicherweise versehentlich auch das .snapshotVerzeichnis (und alle verschachtelten .snapshot-Verzeichnisse) auf: -

find . -name .snapshot -prune -o -name '*.foo'

Der Grund ist (laut Manpage auf meinem System): -

Wenn der angegebene Ausdruck keine der Primärfarben -exec, -ls, -ok oder -print enthält, wird der angegebene Ausdruck effektiv ersetzt durch:

(Given_Ausdruck) -Druck

Das heißt, das zweite Beispiel entspricht der Eingabe des Folgenden, wodurch die Gruppierung von Begriffen geändert wird:

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Dies wurde zumindest unter Solaris 5.10 beobachtet. Nachdem ich ca. 10 Jahre lang verschiedene Geschmacksrichtungen von * nix verwendet habe, habe ich erst kürzlich nach einem Grund gesucht, warum dies auftritt.

crw
quelle
Vielen Dank, dass Sie auf den Unterschied zwischen Verwenden -prunemit und ohne aufmerksam gemacht haben -print!
mcw
3

Prune ist ein nicht wiederkehrender Verzeichniswechsel.

Von der Manpage

Wenn -tiefe nicht gegeben ist, wahr; Wenn es sich bei der Datei um ein Verzeichnis handelt, steigen Sie nicht in dieses Verzeichnis ab. Wenn -tiefe angegeben ist, falsch; Kein Effekt.

Grundsätzlich wird es nicht in Unterverzeichnisse absteigen.

Nehmen Sie dieses Beispiel:

Sie haben die folgenden Verzeichnisse

  • / home / test2
  • / home / test2 / test2

Wenn Sie laufen find -name test2:

Es werden beide Verzeichnisse zurückgegeben

Wenn Sie laufen find -name test2 -prune:

Es wird nur / home / test2 zurückgegeben, da es nicht in / home / test2 absteigt, um / home / test2 / test2 zu finden

AdamW
quelle
nicht 100% wahr: Es ist "Bereinigen, wenn die Bedingung übereinstimmt, und wenn es sich um ein Verzeichnis handelt, nehmen Sie es aus der Aufgabenliste heraus, dh geben Sie es nicht ebenfalls ein". -prune funktioniert auch bei Dateien.
Olivier Dulac
2

Ich bin kein Experte in diesem Bereich (und diese Seite war zusammen mit http://mywiki.wooledge.org/UsingFind sehr hilfreich )

Gerade bemerkt -pathist für einen Pfad, der vollständig mit dem String / Pfad übereinstimmt, der unmittelbar danach kommtfind ( .in diesen Beispielen), wobei -namealle Basisnamen übereinstimmen.

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

blockiert das .git-Verzeichnis in Ihrem aktuellen Verzeichnis ( wie Sie es gefunden haben . )

find . -name .git  -prune -o -name file  -print

blockiert alle .git-Unterverzeichnisse rekursiv.

Beachten Sie, dass das ./ extrem wichtig ist !! -pathmuss mit einem Pfad übereinstimmen, der verankert ist . oder was auch immer direkt nach dem Finden kommt, wenn Sie Übereinstimmungen mit ihm erhalten (von der anderen Seite des oder ' -o'), wird dort wahrscheinlich nicht beschnitten! Ich war mir dessen naiv nicht bewusst und es brachte mich dazu, -path zu verwenden, wenn es großartig ist, wenn Sie nicht alle Unterverzeichnisse mit demselben Basisnamen beschneiden möchten: D.

Sabgenton
quelle
Beachten Sie, wenn Sie sagen, find bla/dann würden Sie -path .git benötigen bla/(oder wenn Sie *stattdessen ein vorne schieben, würde es sich eher wie -name verhalten)
sabgenton
1

Zeigen Sie alles, einschließlich dir selbst, aber nicht den langweiligen Inhalt:

find . -print -name dir -prune
Devon
quelle
0

Wenn Sie alle guten Antworten hier lesen, verstehe ich jetzt, dass die folgenden alle die gleichen Ergebnisse liefern:

find . -path ./dir1\*  -prune -o -print

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

find . -path ./dir1\*  -o -print
#look no prune at all!

Aber der letzte wird viel länger dauern, da er immer noch alles in dir1 sucht. Ich denke, die eigentliche Frage ist, wie -orman unerwünschte Ergebnisse herausfindet, ohne sie tatsächlich zu durchsuchen.

Ich denke also, dass Pflaumen bedeutet, dass vergangene Spiele nicht anständig sind, sondern als erledigt markiert werden ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Dies ist jedoch nicht auf die Wirkung der Aktion '-prune' zurückzuführen (die nur einen weiteren Abstieg verhindert, aber nicht sicher ist) wir ignorieren dieses Element). Stattdessen ist dieser Effekt auf die Verwendung von '-o' zurückzuführen. Da die linke Seite der "oder" -Bedingung für ./src/emacs erfolgreich war, ist es nicht erforderlich, die rechte- zu bewerten. Hand-Seite ('-print') überhaupt für diese bestimmte Datei. "

Sabgenton
quelle
0

findErstellt eine Liste von Dateien. Es wendet das von Ihnen angegebene Prädikat auf jedes an und gibt diejenigen zurück, die bestanden haben.

Diese Idee, die -prunebedeutet, von den Ergebnissen auszuschließen, war für mich wirklich verwirrend. Sie können eine Datei ohne Bereinigung ausschließen:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

Alles was Sie -prunetun müssen, ist das Verhalten der Suche zu ändern. Wenn es sich bei der aktuellen Übereinstimmung um ein Verzeichnis handelt, heißt es "Hey find, die Datei, die Sie gerade abgeglichen haben, steigt nicht hinein" . Es wird nur dieser Baum (aber nicht die Datei selbst) aus der Liste der zu durchsuchenden Dateien entfernt.

Es sollte benannt werden -dont-descend.

Sucher von Bacon
quelle
0

Es gibt einige Antworten; Einige von ihnen sind etwas zu theoretisch. Ich werde gehen, warum ich einmal beschneiden musste , also ist vielleicht die Erklärung " Need-First / Beispiel" für jemanden nützlich :)

Problem

Ich hatte einen Ordner mit ungefähr 20 Knotenverzeichnissen, von denen jedes node_moduleswie erwartet sein Verzeichnis hatte.

Sobald Sie in ein Projekt einsteigen, sehen Sie jedes ../node_modules/module. Aber du weißt wie es ist. Fast jedes Modul hat Abhängigkeiten, also ist das, was Sie betrachten, eher soprojectN/node_modules/moduleX/node_modules/moduleZ...

Ich wollte nicht mit einer Liste ertrinken, die die Abhängigkeit von ...

Wenn ich -d n/ kenne -depth n, hätte es mir nicht geholfen, da sich das Hauptverzeichnis / first node_modules, das ich für jedes Projekt haben wollte, in einer anderen Tiefe befand, wie folgt:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

Wie kann ich dem ersten eine Liste von Pfaden geben, die am ersten enden, node_modulesund zum nächsten Projekt wechseln, um dasselbe zu erhalten?

Eingeben -prune

Wenn Sie hinzufügen -prune, haben Sie immer noch eine rekursive Standardsuche. Jeder "Pfad" wird analysiert, und jeder Fund wird ausgespuckt und findgräbt weiter wie ein guter Kerl. Aber es ist das Ausgraben nach mehr, node_moduleswas ich nicht wollte.

So ist der Unterschied , dass in jedem dieser verschiedenen Pfaden, -prunewird findweiter nach unten zu stoppen zu graben , dass bestimmte Allee , wenn es Ihren Artikel gefunden hat. In meinem Fall der node_modulesOrdner.

Carles Alcolea
quelle