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.
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.
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:
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:
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:
Dann würde Hello, world
zugewiesen (das erste Argument des Skripts) und innerhalb des Skripts könnte ich echo
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. 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.
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:
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.
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):
Leiten Sie das Ergebnis an xargs
, damit Sie einen Befehl für jede Übereinstimmung ausführen können.