So leiten Sie eine Liste der vom Befehl find zurückgegebenen Dateien an cat weiter, um alle Dateien anzuzeigen

204

Ich mache eine findund bekomme dann eine Liste von Dateien. Wie leite ich es an ein anderes Dienstprogramm wie cat(damit cat den Inhalt all dieser Dateien anzeigt) und brauche im Grunde grepetwas aus diesen Dateien.

Devang Kamdar
quelle

Antworten:

340
  1. Weiterleitung an einen anderen Prozess (obwohl dies NICHT das erreicht, was Sie gesagt haben, dass Sie versuchen zu tun):

    command1 | command2
    

    Dadurch wird die Ausgabe von Befehl1 als Eingabe von Befehl2 gesendet

  2. -execauf a find(dies wird tun, was Sie tun möchten - ist aber spezifisch für find)

    find . -name '*.foo' -exec cat {} \;
    

    (Alles zwischen findund -execsind die Suchprädikate, die Sie bereits verwendet haben. {}Ersetzt die bestimmte Datei, die Sie im Befehl gefunden haben ( cat {}in diesem Fall). Dies \;ist das Beenden des -execBefehls.)

  3. Senden Sie die Ausgabe eines Prozesses als Befehlszeilenargumente an einen anderen Prozess

    command2 `command1`
    

    beispielsweise:

    cat `find . -name '*.foo' -print`
    

    (Beachten Sie, dass dies BACK-QUOTES sind, keine regulären Anführungszeichen (unter der Tilde ~ auf meiner Tastatur).) Dadurch wird die Ausgabe von command1in command2als Befehlszeilenargumente gesendet. Beachten Sie jedoch, dass Dateinamen mit Leerzeichen (Zeilenumbrüche usw.) in separate Argumente unterteilt werden.

kenj0418
quelle
2
Katze find -name '*.foo' -printhat großartig für mich gearbeitet ... Danke
Devang Kamdar
Die Backquotes funktionieren hervorragend und sind allgemeiner. Sie können damit auch eine Liste von Dateien aus einer Datei zusammenstellen.
Hazok
11
Beachten Sie, dass Sie in modernen Versionen von findFolgendes schreiben können : find . -name '*.foo' -exec cat {} +, wobei +angegeben wird, dass findso viele Dateinamen wie möglich in einem einzigen Befehlsaufruf zusammengefasst werden sollen. Dies ist sehr nützlich (es behandelt Leerzeichen usw. in Dateinamen, ohne auf -print0und zurückzugreifen xargs -0).
Jonathan Leffler
18
Nicht erwähnt:find . -name '*.foo' | xargs cat
Eintopf Quadrat
3
Nur um die Antwort von @stewSquared hinzuzufügen: Um alle Zeilen in Dateien zu finden, die eine bestimmte Zeichenfolge enthalten, tun Siefind . -name '*.foo' | xargs cat | grep string
Bim
84

Moderne Version

POSIX 2008 hat den +Marker hinzugefügt , findwas bedeutet, dass jetzt automatisch so viele Dateien wie möglich in einer einzigen Befehlsausführung zusammengefasst werden, ähnlich wie dies der xargsFall ist, jedoch mit einer Reihe von Vorteilen:

  1. Sie müssen sich keine Gedanken über ungerade Zeichen in den Dateinamen machen.
  2. Sie müssen sich keine Sorgen machen, dass der Befehl mit null Dateinamen aufgerufen wird.

Das Problem mit xargsdem Dateinamen ist ein Problem ohne die -0Option, und das Problem "Auch ohne Dateinamen ausführen" ist ein Problem mit oder ohne die -0Option - aber GNU xargshat die Option -roder --no-run-if-empty, um dies zu verhindern. Außerdem reduziert diese Notation die Anzahl der Prozesse, nicht dass Sie wahrscheinlich den Leistungsunterschied messen. Daher könnte man vernünftigerweise schreiben:

find . -exec grep something {} +

Klassische Version

find . -print | xargs grep something

Wenn Sie unter Linux arbeiten oder über GNU findund xargsBefehle verfügen , verwenden Sie -print0mit findund -0mit xargsDateinamen, die Leerzeichen und andere ungerade Zeichen enthalten.

find . -print0 | xargs -0 grep something

Optimieren der Ergebnisse von grep

Wenn Sie die Dateinamen (nur den Text) nicht möchten, fügen Sie eine entsprechende Option hinzu grep(normalerweise -hzum Unterdrücken von 'Überschriften'). Um absolut zu gewährleisten, dass der Dateiname von gedruckt wird grep(auch wenn nur eine Datei gefunden wird oder der letzte Aufruf von grepnur 1 Dateinamen enthält), fügen Sie ihn /dev/nullzur xargsBefehlszeile hinzu, sodass immer mindestens zwei Dateinamen vorhanden sind.

Jonathan Leffler
quelle
Beachten Sie für diejenigen, die so verwirrt sind wie ich, dass auf diese Weise zuerst die gesamte Ausgabe von find und dann die Ausgabe von find ausgegeben wird xargs grep something.
Eric Hu
3
@EricHu: Ich kann sehen, dass Sie verwirrt sind, aber es macht nicht das, was Sie sagen, zumindest nicht auf einem mir bekannten Unix-basierten System. Der Ausgang von findwird an den Standardeingang von weitergeleitet xargs. Das xargsProgramm liest seine Standardeingabe, teilt die Eingabe im Leerraum (Leerzeichen, Zeilenumbrüche, Tabulatoren usw.) auf, hängt eine Reihe von Wörtern an den Befehl an grep somethingund führt die Befehlszeile aus. xargsLesen Sie dann die Eingabe weiter und führen Sie die Befehle aus, bis die Eingabe aufgebraucht ist. xargsführt den grepBefehl so oft aus, wie es für die Eingabe erforderlich ist ( findin diesem Beispiel).
Jonathan Leffler
Ah, mein Fehler, dies ist die Verwendung von grep, um in jeder übereinstimmenden Datei zu suchen. Ich wollte einfach die Ausgabe von find mit grep filtern
Eric Hu
1
Fehler gehen bei allen gut erzogenen Befehlen zum Standardfehler (Dateideskriptor 2). Wenn Sie stderr umleiten, um /dev/nulldie Fehlermeldungen zu verlieren.
Jonathan Leffler
1
Dies hat auch den Vorteil, dass es mit Leerzeichen im Dateipfad besser funktioniert. Sogar 'sed'ing "" -> "\" bricht es mit dem `, aber mit xargs funktioniert es perfekt
JZL003
36

Es gibt einige Möglichkeiten, die Liste der vom findBefehl zurückgegebenen Dateien an den Befehl zu übergeben cat, obwohl technisch nicht alle Piping verwenden und keine direkt an cat.

  1. Am einfachsten ist es, backticks ( `) zu verwenden:

    cat `find [whatever]`
    

    Dies nimmt die Ausgabe von findund platziert sie effektiv in der Befehlszeile von cat. Dies funktioniert nicht gut, wenn findzu viel ausgegeben wird (mehr als in eine Befehlszeile passen kann) oder wenn die Ausgabe Sonderzeichen enthält (wie Leerzeichen).

  2. In einigen Shells, einschließlich bash, kann man $()anstelle von Backticks verwenden:

    cat $(find [whatever])
    

    Dies ist weniger portabel, aber verschachtelbar. Abgesehen davon hat es so ziemlich die gleichen Einschränkungen wie Backticks.

  3. Da das Ausführen anderer Befehle für das, was gefunden wurde, häufig verwendet findwird, verfügt find über eine -execAktion, die einen Befehl für jede gefundene Datei ausführt:

    find [whatever] -exec cat {} \;
    

    Das {}ist ein Platzhalter für den Dateinamen und \;markiert das Ende des Befehls (es ist möglich, dass danach andere Aktionen ausgeführt werden -exec.)

    Dies wird cateinmal für jede einzelne Datei ausgeführt, anstatt eine einzelne Instanz auszuführen, in der catmehrere Dateinamen übergeben werden. Dies kann ineffizient sein und möglicherweise nicht das gewünschte Verhalten für einige Befehle aufweisen (obwohl dies für in Ordnung ist cat). Die Syntax ist auch umständlich einzugeben - Sie müssen das Semikolon umgehen, da das Semikolon für die Shell etwas Besonderes ist!

  4. Einige Versionen von find(vor allem der GNU - Version) können Sie ersetzen ;mit +zu verwenden -exec‚s Zufügen - Modus zu weniger Instanzen ausführen cat:

    find [whatever] -exec cat {} +
    

    Dadurch werden an jeden Aufruf von mehrere Dateinamen übergeben cat, was effizienter sein kann.

    Beachten Sie jedoch, dass nicht garantiert wird, dass ein einzelner Aufruf verwendet wird. Wenn die Befehlszeile zu lang wäre, werden die Argumente auf mehrere Aufrufe von verteilt cat. Für catdiese ist wahrscheinlich keine große Sache, aber für einige andere Befehle kann dies das Verhalten in unerwünschter Weise verändern. Auf Linux-Systemen ist die Beschränkung der Befehlszeilenlänge recht groß, sodass die Aufteilung in mehrere Aufrufe im Vergleich zu einigen anderen Betriebssystemen eher selten ist.

  5. Der klassische / tragbare Ansatz besteht darin, Folgendes zu verwenden xargs:

    find [whatever] | xargs cat
    

    xargsführt den angegebenen Befehl aus ( catin diesem Fall) und fügt Argumente hinzu, die auf dem basieren, was von stdin gelesen wird. Genau wie -execbei +wird dadurch die Befehlszeile bei Bedarf unterbrochen. Das heißt, wenn findzu viel Ausgabe erzeugt wird, wird sie catmehrmals ausgeführt. Wie bereits in diesem Abschnitt -execerwähnt, gibt es einige Befehle, bei denen diese Aufteilung zu einem anderen Verhalten führen kann. Beachten Sie, dass die Verwendung auf xargsdiese Weise Probleme mit Leerzeichen in Dateinamen hat, da xargsnur Leerzeichen als Trennzeichen verwendet werden.

  6. Die robusteste, tragbarste und effizienteste Methode verwendet außerdem xargs:

    find [whatever] -print0 | xargs -0 cat
    

    Das -print0Flag weist findan, \0Trennzeichen (Nullzeichen) zwischen Dateinamen zu verwenden, und das -0Flag weist xargsan, diese \0Trennzeichen zu erwarten . Dies hat ein ziemlich identisches Verhalten wie der -exec... +Ansatz, ist jedoch portabler (aber leider ausführlicher).

Laurence Gonsalves
quelle
Die Backtick-Methode ist großartig, da sie auch für andere Befehle wie funktioniert ls.
Martin Braun
@Martin Braun using Funktioniert $()auch mit anderen Befehlen als find .
Laurence Gonsalves
Danke, gut zu wissen, ich habe nach (1) irgendwie aufgehört zu lesen, weil es meinen Bedürfnissen entspricht, da ich mich nicht mit Sonderzeichen wie Leerzeichen und dergleichen beschäftige.
Martin Braun
9

Um dies zu erreichen (mit bash), würde ich wie folgt vorgehen:

cat $(find . -name '*.foo')

Dies ist als "Befehlsersetzung" bekannt und entfernt standardmäßig den Zeilenvorschub, was sehr praktisch ist!

Weitere Infos hier

Stphane
quelle
6

Klingt für mich nach einem Job für ein Shell-Skript:

for file in 'find -name *.xml'
do
   grep 'hello' file
done

oder etwas ähnliches

Gandalf
quelle
2
Dies ist eine gültige, wenn auch nicht unbedingt optimale Antwort auf die Frage.
Jonathan Leffler
1
... ja, aber es ist großartig, wenn Sie eine große Datei mit Dateinamen möchten.
11.
1
Das gefällt mir am besten. Ein Loop-Block wie dieser lässt Raum für andere Dinge.
Kakyo
4

Hier ist meine Möglichkeit, Dateinamen zu finden, die Inhalte enthalten, an denen ich interessiert bin, nur eine einzige Bash-Zeile, die auch Leerzeichen in Dateinamen gut verarbeitet:

find . -name \*.xml | while read i; do grep '<?xml' "$i" >/dev/null; [ $? == 0 ] && echo $i; done
Greg
quelle
3

Ich benutze so etwas:

find . -name <filename> -print0 | xargs -0 cat | grep <word2search4>

" -print0" Argumente für "find" und " -0" Argumente für "xargs" werden benötigt, um Leerzeichen in Dateipfaden / -namen korrekt zu behandeln.

Ekinak
quelle
2

Der Befehl find hat ein Argument -exec, das Sie für solche Dinge verwenden können. Sie können das grep einfach direkt damit ausführen.

Zum Beispiel ( von hier aus andere gute Beispiele auf dieser Seite ):

find . -exec grep "www.athabasca" '{}' \; -print 
Chad Birch
quelle
2

Hier ist mein Schuss für den allgemeinen Gebrauch:

grep YOURSTRING `find .`

Der Dateiname wird gedruckt

zakki
quelle
2

In Bash wäre Folgendes angemessen:

find /dir -type f -print0 | xargs -0i cat {} | grep whatever

Dadurch werden alle Dateien im /dirVerzeichnis gefunden und die Dateinamen xargssicher weitergeleitet, wodurch sie sicher fahren grep.

Das Überspringen xargsist keine gute Idee, wenn Sie viele tausend Dateien haben /dir. catwird aufgrund übermäßiger Länge der Argumentliste unterbrochen. xargswird das alles für Sie klären.

Das -print0zu findvernetzende Argument mit dem -0Argument, xargsDateinamen mit Leerzeichen richtig zu behandeln. Mit dem -iArgument to xargskönnen Sie den Dateinamen an der erforderlichen Stelle in die catBefehlszeile einfügen . Die Klammern werden durch den Dateinamen ersetzt, der in den catBefehl von weitergeleitet wird find.

McClain Looney
quelle
0

Das funktioniert bei mir

find _CACHE_* | while read line; do
    cat "$line" | grep "something"
done
Steven Penny
quelle
0

Verwenden Sie ggrep .

ggrep -H -R -I "mysearchstring" *

um nach einer Datei unter Unix zu suchen, die Text enthält, der sich im aktuellen Verzeichnis oder in einem Unterverzeichnis befindet

Underverse
quelle
0

Dadurch werden der Name und der Inhalt von Dateien nur rekursiv gedruckt.

find . -type f -printf '\n\n%p:\n' -exec cat {} \;

Bearbeiten (verbesserte Version): Hiermit werden Name und Inhalt von Textdateien (ASCII) nur rekursiv gedruckt.

find . -type f -exec grep -Iq . {} \; -print | xargs awk 'FNR==1{print FILENAME ":" $0; }'

Noch ein Versuch

find . -type f -exec grep -Iq . {} \; -printf "\n%p:" -exec cat {} \;
Prashant Adlinge
quelle
-1

Versuchen Sie, Text in Dateien zu finden? Sie können einfach grep dafür verwenden ...

grep searchterm *
Scott Anderson
quelle
-1

Zum Auflisten und Anzeigen des Inhalts aller abc.def-Dateien auf einem Server in den Verzeichnissen / ghi und / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec ls {} \; -exec cat {} \;

Um die abc.def-Dateien mit kommentierten Einträgen aufzulisten und anzuzeigen, sehen Sie sich diese Einträge in den Verzeichnissen / ghi und / jkl an

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec grep -H ^# {} \;
Sharjeel
quelle