Wie füge ich eine Pipe hinzu? in meinem Linux-Befehl -exec finden?

220

Das funktioniert nicht. Kann dies in find gemacht werden? Oder muss ich xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;
irgendein Typ
quelle

Antworten:

145

Die Aufgabe, das Pipe-Symbol als Anweisung zum Ausführen mehrerer Prozesse zu interpretieren und die Ausgabe eines Prozesses in die Eingabe eines anderen Prozesses zu leiten, liegt in der Verantwortung der Shell (/ bin / sh oder gleichwertig).

In Ihrem Beispiel können Sie entweder Ihre Top-Level-Shell verwenden, um die Rohrleitungen wie folgt auszuführen:

find -name 'file_*' -follow -type f -exec zcat {} \; | agrep -dEOE 'grep'

In Bezug auf die Effizienz kostet dieses Ergebnis einen Aufruf des Funds, zahlreiche Aufrufe von zcat und einen Aufruf der Zustimmung.

Dies würde dazu führen, dass nur ein einziger zustimmender Prozess erzeugt wird, der die gesamte Ausgabe verarbeitet, die durch zahlreiche Aufrufe von zcat erzeugt wird.

Wenn Sie aus irgendeinem Grund mehrmals einverstanden sein möchten, können Sie Folgendes tun:

find . -name 'file_*' -follow -type f \
    -printf "zcat %p | agrep -dEOE 'grep'\n" | sh

Dadurch wird eine Liste von Befehlen erstellt, die Pipes zum Ausführen verwenden, und diese dann an eine neue Shell gesendet, um sie tatsächlich auszuführen. (Das Weglassen des letzten "| sh" ist eine gute Möglichkeit, solche Befehlszeilen zu debuggen oder trocken auszuführen.)

In Bezug auf die Effizienz kostet dieses Ergebnis einen Aufruf des Funds, einen Aufruf des sh, zahlreiche Aufrufe von zcat und zahlreiche Aufrufe der Zustimmung.

Die effizienteste Lösung in Bezug auf die Anzahl der Befehlsaufrufe ist der Vorschlag von Paul Tomblin:

find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'

... was einen Aufruf von find, einen Aufruf von xargs, einige Aufrufe von zcat und einen Aufruf von acceptp kostet.

Rolf W. Rasmussen
quelle
1
Ein weiterer Vorteil von xargs wäre, dass Sie es mit der modernen Multi-Core-CPU noch schneller machen können, indem Sie den -P-Schalter (-P 0) verwenden.
Flolo
Ja, der -P-Schalter ist in der Tat eine gute Möglichkeit, die Ausführung im Allgemeinen zu beschleunigen. Leider besteht die Gefahr, dass die Ausgabe paralleler zcat-Prozesse in eine akzeptable Verschachtelung geleitet wird, was sich auf das Ergebnis auswirken würde. Dieser Effekt kann demonstriert werden mit: echo -e "1 \ n2" | xargs -P 0 -n 1 ja | uniq
Rolf W. Rasmussen
@Adam, ich habe deine vorgeschlagene Änderung vorgenommen.
Paul Tomblin
für die Sie den Befehl splendid xjobs (ursprünglich von Solaris) installieren können
siehe
4
Eine einfachere und allgemeinere Antwort finden Sie unter stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Eric O Lebigot
278

die lösung ist einfach: über sh ausführen

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
flolo
quelle
17
Was das OP erreichen wollte, kann mit den obigen Vorschlägen erreicht werden, aber dies ist derjenige, der die gestellte Frage tatsächlich beantwortet. Es gibt Gründe, dies so zu tun - exec ist viel leistungsfähiger als nur die von find zurückgegebenen Dateien zu bearbeiten, insbesondere in Kombination mit test. Zum Beispiel: find geda-gaf / -typ d -exec bash -c 'DIR = {}; [[$ (finde $ DIR -maxdepth 1 | xargs grep -i Gewürz | wc -l) -ge 5]] && echo $ DIR '\; Gibt alle Verzeichnisse im Suchpfad zurück, die insgesamt mehr als 5 Zeilen enthalten, unter allen Dateien in diesem Verzeichnis, die das Wort spice enthalten
swarfrat
3
Beste Antwort. Das Greppen der gesamten Ausgabe (wie andere Antworten vermuten lassen) ist nicht dasselbe wie das Grepen jeder Datei. Tipp: Anstelle von sh können Sie jede andere Shell verwenden, die Sie möchten (ich habe das mit bash versucht und es läuft in Ordnung).
Pagliuca
1
Stellen Sie sicher, dass Sie die -cOption nicht übersehen . Andernfalls erhalten Sie eine rätselhafte No such file or directoryFehlermeldung.
Asmaier
Hier ist ein großartiger ps-Ersatz, der find mit Piping in einer ausführenden Shell verwendet: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
Parität3
1
Beispiel für das Finden und Umbenennen von Dateien mit sed unter Verwendung eines regulären Ausdrucks find -type f -name '*.mdds' -exec sh -c "echo {} | sed -e 's/_[0-9]\+//g' | xargs mv {}" \;
Rostfrei
16
find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'
Paul Tomblin
quelle
In der Hoffnung, -print und xargs aus Effizienzgründen zu vermeiden. Vielleicht ist das wirklich mein Problem: find kann keine Pipeline-Befehle über -exec
irgendwann
Dies funktioniert nicht mit Dateien, deren Namen Leerzeichen enthalten. Um dies zu beheben, ersetzen Sie -print durch -print0 und fügen Sie xargs die Option -0 hinzu
Adam Rosenfield
2
@ Someguy - Was? Xargs aus Effizienzgründen vermeiden? Das Aufrufen einer Instanz von zcat und das Übergeben einer Liste mit mehreren Dateien ist weitaus effizienter als das Ausführen einer neuen Instanz von zcat für jede gefundene Datei.
Sherm Pendley
@Adam - Ich habe deine vorgeschlagene Änderung vorgenommen. 99% der Zeit, in der ich Funde mache, befindet es sich in meinen Quellcode-Verzeichnissen, und keine der Dateien dort enthält Leerzeichen, sodass ich mich nicht um print0 kümmere. Jetzt erinnere ich mich in meinem Dokumentenverzeichnis an den print0.
Paul Tomblin
10

Sie können auch zu einer whileSchleife weiterleiten, die mehrere Aktionen für die gefundene Datei ausführen kann find. Hier ist eine, um in jarArchiven nach einer bestimmten Java-Klassendatei in einem Ordner mit einer großen Anzahl von jarDateien zu suchen

find /usr/lib/eclipse/plugins -type f -name \*.jar | while read jar; do echo $jar; jar tf $jar | fgrep IObservableList ; done

Der entscheidende Punkt ist, dass die whileSchleife mehrere Befehle enthält, die auf den übergebenen Dateinamen verweisen, der durch ein Semikolon getrennt ist. Diese Befehle können Pipes enthalten. In diesem Beispiel gebe ich den Namen der übereinstimmenden Datei wieder und liste dann auf, was in der Archivfilterung für einen bestimmten Klassennamen enthalten ist. Die Ausgabe sieht aus wie:

/usr/lib/eclipse/plugins/org.eclipse.core.contenttype.source_3.4.1.R35x_v20090826-0451.jar /usr/lib/eclipse/plugins/org.eclipse.core.databinding.observable_1.2.0.M20090902-0800 .jar org / eclipse / core / datenbindung / beobachtbar / liste / IObservableList .class /usr/lib/eclipse/plugins/org.eclipse.search.source_3.5.1.r351_v20090708-0800.jar / usr / lib / eclipse / plugins / org.eclipse.jdt.apt.core.source_3.3.202.R35x_v20091130-2300.jar /usr/lib/eclipse/plugins/org.eclipse.cvs.source_1.0.400.v201002111343.jar / usr / lib / eclipse / plugins / org.eclipse.help.appserver_3.1.400.v20090429_1800.jar

In meiner Bash-Shell (xubuntu10.04 / xfce) wird der übereinstimmende Klassenname wirklich fett gedruckt, da fgrepdie übereinstimmende Zeichenfolge hervorgehoben wird. Dies macht es wirklich einfach, die Liste von Hunderten von jarDateien zu durchsuchen, die durchsucht wurden, und leicht Übereinstimmungen zu sehen.

Unter Windows können Sie dasselbe tun mit:

for /R %j in (*.jar) do @echo %j & @jar tf %j | findstr IObservableList

Beachten Sie, dass unter Windows das Befehlstrennzeichen '&' not ';' ist. und dass das '@' das Echo des Befehls unterdrückt, um eine ordentliche Ausgabe zu liefern, genau wie die Linux-Find-Ausgabe oben; Obwohl findstrdie übereinstimmende Zeichenfolge nicht fett gedruckt ist, müssen Sie sich die Ausgabe etwas genauer ansehen, um den Namen der übereinstimmenden Klasse zu sehen. Es stellt sich heraus, dass der Windows-Befehl 'for' einige Tricks kennt, wie das Durchlaufen von Textdateien ...

genießen

simbo1905
quelle
2

Ich fand, dass das Ausführen eines String-Shell-Befehls (sh -c) am besten funktioniert, zum Beispiel:

find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;
Andrew Khoury
quelle
0

Wenn Sie nach einer einfachen Alternative suchen, können Sie dies mit einer Schleife tun:

for i in $(find -name 'file_*' -follow -type f);do zcat $i | agrep -dEOE 'grep');done

oder allgemeinere und leicht verständliche Form:

for i in $(YOUR_FIND_COMMAND);do YOUR_EXEC_COMMAND_AND_PIPES );done

und ersetzen Sie {} durch $ i in YOUR_EXEC_COMMAND_AND_PIPES

Louis Gagnon
quelle