führe eine Operation für * jedes * Element aus, das von grep aufgelistet wird

8

Wie kann ich für jedes Element, das von grep einzeln aufgeführt wird, eine Operation durchführen?

Hintergrund:

Ich verwende grep, um alle Dateien aufzulisten, die ein bestimmtes Muster enthalten:

%Vor%

Ich möchte alle aufgelisteten Dateien löschen , aber auch alle Dateien, die denselben Dateinamen aber eine andere Erweiterung haben: .extension2 .

Ich habe versucht, die Pipe zu benutzen, aber es scheint die Ausgabe von grep als Ganzes zu nehmen.

In find gibt es die Option -exec , aber grep hat nichts dergleichen.

    
speendo 13.03.2012, 11:01
quelle

4 Antworten

14

Wenn ich Ihre Spezifikation verstehe, wollen Sie:

%Vor%

Das ist im Wesentlichen dasselbe wie das, was @ triplees Kommentar beschreibt, außer dass es newline-sicher ist.

Was ist hier los?

grep mit --null gibt die Ausgabe mit Nullen anstelle von Newline zurück. Da Dateinamen neue Zeilen enthalten können, ist es mit newline unmöglich, die Ausgabe von grep sicher zu analysieren, aber null ist kein gültiges Zeichen in einem Dateinamen und bildet daher ein nettes Trennzeichen.

xargs nimmt einen Strom von durch Zeilentrennzeichen getrennten Elementen und führt einen bestimmten Befehl aus, wobei so viele dieser Elemente (einer wie jeder Parameter) an einen gegebenen Befehl übergeben werden (oder an echo , wenn kein Befehl angegeben wird). Also wenn du gesagt hast:

%Vor%

xargs würde echo one 'two three' four ausführen. Dies ist nicht sicher für Dateinamen, da Dateinamen wiederum eingebettete Zeilenumbrüche enthalten können.

Der -0 -Schalter auf xargs ändert es von der Suche nach einem Newline-Begrenzer zu einem Null-Begrenzer. Dies stimmt mit der Ausgabe überein, die wir von grep --null erhalten haben, und macht es sicher für die Verarbeitung einer Liste von Dateinamen.

Normalerweise hängt xargs die Eingabe einfach an das Ende eines Befehls an. Der Schalter -I auf xargs ändert dies, um die angegebene Ersetzungszeichenfolge durch die Eingabe zu ersetzen. Um die Idee zu verstehen, versuche dieses Experiment:

%Vor%

Beachten Sie den Unterschied zum früheren Befehl printf | xargs .

Im Falle meiner Lösung ist der von mir ausgeführte Befehl bash , dem ich -c übergebe. Der Schalter -c bewirkt, dass bash die Befehle im folgenden Argument ausführt (und dann beendet), anstatt eine interaktive Shell zu starten. Der nächste Block 'rm "" "${1%.*}.extension2"' ist das erste Argument für -c und ist das Skript, das von bash ausgeführt wird. Alle Argumente, die dem Skriptargument auf -c folgen, werden dem Skript als Argumente zugewiesen. Dies, wenn ich sagen würde:

%Vor%

Dann würde Hello, world echo zugewiesen (das erste Argument des Skripts) und innerhalb des Skripts könnte ich -- zurückgeben.

Da {} normalerweise für den Skriptnamen reserviert ist, übergebe ich einen Dummy-Wert (in diesem Fall xargs ) als erstes Argument und schreibe dann an Stelle des zweiten Arguments xargs , was das ist Ersetzungszeichenfolge, die ich für grep angegeben habe. Dies wird durch bash ersetzt, wobei jeder Dateiname von rm ausgegeben wird, bevor ausgeführt wird.

Das Mini-Shell-Skript mag kompliziert aussehen, aber es ist eher trivial. Erstens ist das gesamte Skript in Anführungszeichen gesetzt, um zu verhindern, dass die aufrufende Shell es interpretiert. Innerhalb des Skripts rufe ich ${1%.*}.extension2 auf und übergebe es zwei zu entfernende Dateinamen: das -Argument, das der Dateiname war, der übergeben wurde, als die Ersatzzeichenfolge oben ersetzt wurde, und %.* . Letzteres ist eine Parametersubstitution für die Variable % . Der wichtige Teil ist .* was sagt

  • .extension2 "Entspricht dem Ende der Variablen und entfernt die kürzeste Zeichenfolge, die dem Muster entspricht.
  • %code% Das Muster ist eine einzelne Periode gefolgt von allem.

Dadurch wird die Erweiterung (falls vorhanden) effektiv vom Dateinamen entfernt. Sie können den Effekt selbst beobachten:

%Vor%

Da die Erweiterung entfernt wurde, verkette ich die gewünschte alternative Erweiterung %code% mit dem Namen der entfernten Datei, um den alternativen Dateinamen zu erhalten.

    
Sorpigal 13.03.2012, 11:44
quelle
4

Wenn dies das gewünschte Ergebnis liefert, leiten Sie die Ausgabe durch / bin / sh.

%Vor%

Oder wenn sed dich juckt:

%Vor%

Entfernen Sie echo , wenn die Ausgabe wie die Befehle aussieht, die Sie ausführen möchten.

Aber Sie können dies auch mit find tun:

%Vor%

Dabei ist ... entweder das sed-Skript oder die drei Zeilen von while bis done .

Die Idee hier ist, dass find ... nun, Dinge auf der Grundlage der Qualifikationsmerkmale "findet", die Sie ihr geben - nämlich, dass die Dinge mit dem Datei-Glob "* .ext" übereinstimmen UND dass das Ergebnis der "exec" ist erfolgreich. Das -q veranlasst grep, nach RE in {} zu suchen (die Datei wird von find geliefert) und endet mit einem TRUE oder FALSE, ohne eine eigene Ausgabe zu erzeugen.

Der einzige Unterschied zwischen find und grep besteht darin, dass Sie finds großartige Sammlung von Bedingungen nutzen können, um Ihre Suche bei Bedarf weiter einzuschränken. man find für Details. Standardmäßig sucht find in Unterverzeichnissen.

    
ghoti 13.03.2012 11:54
quelle
2

Sie können die Liste nach xargs leiten:

%Vor%

Wie für den zweiten Satz von Dateien mit einer anderen Erweiterung, würde ich dies tun (wie üblich xargs echo rm beim Testen verwenden, um einen Trockenlauf zu machen; ich habe es nicht getestet, es funktioniert möglicherweise nicht korrekt mit Dateinamen mit Räume in ihnen):

%Vor%     
Eduardo Ivanec 13.03.2012 11:09
quelle
0

Leiten Sie das Ergebnis an xargs , damit Sie einen Befehl für jede Übereinstimmung ausführen können.

    
Anders Abel 13.03.2012 11:03
quelle

Tags und Links