299 Stimmen

Wie füge ich ein Pipe | in meinen Linux-Befehl find -exec ein?

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

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

399voto

flolo Punkte 14497

Die Lösung ist einfach: per sh ausführen

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;

28 Stimmen

Das, was der Auftraggeber erreichen wollte, kann mit den oben genannten Vorschlägen erreicht werden, aber dies ist derjenige, der die gestellte Frage tatsächlich beantwortet. Es gibt Gründe, es auf diese Weise zu tun - exec ist viel leistungsfähiger als die Dateien, die von find zurückgegeben werden, insbesondere in Kombination mit test. Zum Beispiel: find geda-gaf/ -type d -exec bash -c 'DIR={}; [[ $(find $DIR -maxdepth 1 |xargs grep -i spice |wc -l) -ge 5 ]] && echo $DIR' \; Gibt alle Verzeichnisse im Suchpfad zurück, die insgesamt mehr als 5 Zeilen von allen Dateien in diesem Verzeichnis enthalten, die das Wort spice

7 Stimmen

Beste Antwort. Das Grepen der gesamten Ausgabe (wie andere Antworten nahelegen) ist nicht dasselbe wie das Grepen jeder einzelnen Datei. Tipp: Anstelle von sh können Sie eine beliebige andere Shell verwenden (ich habe das mit bash ausprobiert und es läuft gut).

6 Stimmen

Achten Sie darauf, dass Sie nicht die -c Option. Andernfalls erhalten Sie eine verwirrende No such file or directory Fehlermeldung.

167voto

Rolf W. Rasmussen Punkte 1839

Die Aufgabe, das Pipe-Symbol als Anweisung zu interpretieren, mehrere Prozesse laufen zu lassen 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 das Piping wie folgt durchzuführen:

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

Im Hinblick auf die Effizienz kostet dies einen Aufruf von find, zahlreiche Aufrufe von zcat und einen Aufruf von agrep.

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

Wenn Sie aus irgendeinem Grund agrep mehrfach aufrufen möchten, können Sie das tun:

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

Dies erstellt eine Liste von auszuführenden Befehlen mit Hilfe von Pipes und sendet diese dann an eine neue Shell, damit sie tatsächlich ausgeführt werden. (Das Weglassen des abschließenden "| sh" ist ein guter Weg, um solche Befehlszeilen zu debuggen oder Probeläufe durchzuführen).

Was die Effizienz betrifft, so kostet dies einen Aufruf von find, einen Aufruf von sh, zahlreiche Aufrufe von zcat und zahlreiche Aufrufe von agrep.

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

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

... das kostet einen Aufruf von find, einen Aufruf von xargs, ein paar Aufrufe von zcat und einen Aufruf von agrep.

2 Stimmen

Ein weiterer Vorteil von xargs ist, dass man es mit modernen Mehrkern-CPUs noch weiter beschleunigen kann, indem man den -P Schalter (-P 0) verwendet.

1 Stimmen

Ja, der Schalter -P ist in der Tat eine gute Möglichkeit, die Ausführung im Allgemeinen zu beschleunigen. Leider besteht die Gefahr, dass die Ausgaben paralleler zcat-Prozesse in agrep verschachtelt werden, was das Ergebnis beeinflussen würde. Dieser Effekt kann mit echo -e "1" demonstriert werden. \n2 " | xargs -P 0 -n 1 yes | uniq

0 Stimmen

@Adam, ich habe die von Ihnen vorgeschlagene Änderung vorgenommen.

17voto

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

0 Stimmen

Ich hoffe, dass ich -print und xargs aus Gründen der Effizienz vermeiden kann. Vielleicht ist das wirklich mein Problem: find kann nicht mit gepipten Befehlen durch -exec umgehen

0 Stimmen

Dies funktioniert nicht mit Dateien, die Leerzeichen im Namen haben; um dies zu beheben, ersetzen Sie -print durch -print0 und fügen Sie die Option -0 zu xargs hinzu

2 Stimmen

@someguy - Was? Vermeiden Sie xargs aus Effizienzgründen? Eine Instanz von zcat aufzurufen und ihr eine Liste von mehreren Dateien zu übergeben, ist weit effizienter, als für jede gefundene Datei eine neue Instanz des Programms auszuführen.

12voto

simbo1905 Punkte 5650

Sie können auch eine Leitung zu einer while Schleife, die mehrere Aktionen mit der Datei durchführen kann, die find verortet. Hier ist also einer zum Reinschauen jar Archive für eine bestimmte Java-Klassendatei in einem Ordner mit einer großen Anzahl von jar Dateien

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

der wichtigste Punkt ist, dass die while Schleife enthält mehrere Befehle, die auf den übergebenen Dateinamen verweisen, getrennt durch Semikolon, und diese Befehle können Pipes enthalten. In diesem Beispiel gebe ich also den Namen der passenden Datei aus und liste dann auf, was sich im Archiv befindet, indem ich nach einem bestimmten Klassennamen filtere. Die Ausgabe sieht wie folgt aus:

/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/databinding/observable/list/ 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 tatsächlich fett dargestellt, da die fgrep hebt die übereinstimmende Zeichenkette hervor; dies macht es sehr einfach, die Liste von Hunderten von jar Dateien, die durchsucht wurden, und sehen Sie leicht alle Übereinstimmungen.

unter Windows können Sie das Gleiche tun:

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

Beachten Sie, dass unter Windows das Befehlstrennzeichen '&' und nicht ';' ist und dass das '@' das Echo des Befehls unterdrückt, um eine ordentliche Ausgabe zu erhalten, genau wie die obige Linux-Find-Ausgabe; obwohl findstr macht die übereinstimmende Zeichenkette nicht fett, so dass Sie sich die Ausgabe etwas genauer ansehen müssen, um den übereinstimmenden Klassennamen zu erkennen. Es stellt sich heraus, dass der Windows-Befehl "for" einige Tricks kennt, wie z. B. das Durchlaufen von Textdateien in einer Schleife...

genießen Sie

5voto

Louis Gagnon Punkte 99

Wenn Sie eine einfache 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 in einer allgemeineren und leichter verständlichen Form:

for i in $(YOUR_FIND_COMMAND); do
  YOUR_EXEC_COMMAND_AND_PIPES
done

und ersetzen Sie alle {} durch $i in YOUR_EXEC_COMMAND_AND_PIPES

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X