Verwenden von sed und grep / egrep zum Suchen und Ersetzen

97

Ich verwende egrep -Rgefolgt von einem regulären Ausdruck, der ungefähr 10 Gewerkschaften enthält, also wie: .jpg | .png | .gifusw. Dies funktioniert gut, jetzt möchte ich alle gefundenen Zeichenfolgen durch ersetzen.bmp

Ich dachte an so etwas

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

Das Problem hier ist also, wie ich einen ähnlichen regulären Union-Ausdruck in sedmache und wie ich ihm sage, dass er die Änderungen an der Datei speichern soll, von der er die Eingabe erhalten hat.

Ori
quelle
2
Ich habe diese Frage gefunden, als ich nach Möglichkeiten gesucht habe, mehrere Dateien in einer Verzeichnishierarchie zu suchen und zu ersetzen. Versuchen Sie für andere in meiner Situation rpl .
Titandecoy
danke rpl funktioniert und ist wirklich leicht zu merken .. nur rpl old_string new_string target_files.
Cesarpachon

Antworten:

183

Verwenden Sie diesen Befehl:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: Finden Sie passende Zeilen mit erweiterten regulären Ausdrücken

    • -l: Nur übereinstimmende Dateinamen auflisten

    • -R: Durchsuchen Sie rekursiv alle angegebenen Verzeichnisse

    • -Z: \0als Datensatztrennzeichen verwenden

    • "\.jpg|\.png|\.gif": mit einer der Zeichenfolgen übereinstimmen ".jpg", ".gif"oder".png"

    • .: Starten Sie die Suche im aktuellen Verzeichnis

  • xargs: Führen Sie einen Befehl mit dem Argument stdin aus

    • -0: \0als Datensatztrennzeichen verwenden. Dies ist wichtig , die passen -Zvon egrepund durch Leerzeichen und Zeilenumbrüche in Eingabedateinamen werden getäuscht zu vermeiden.

    • -l: Verwenden Sie eine Zeile pro Befehl als Parameter

  • sed: the s tream ed itor

    • -i: Ersetzen Sie die Eingabedatei durch die Ausgabe, ohne eine Sicherungskopie zu erstellen

    • -e: Verwenden Sie das folgende Argument als Ausdruck

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': Alle Vorkommen der Saiten ersetzen ".jpg", ".gif"oder ".png"mit".bmp"

David Schmitt
quelle
es funktioniert alles außer dem | im sed Teil. Ich verstehe nicht warum, da es Sinn macht ... der -l Teil von xargs hat mir Fehler gegeben, also habe ich es herausgenommen, könnte das zusammenhängen?
Ori
Ich habe festgestellt, dass dieser Befehl am Ende aller von ihm verarbeiteten Dateien eine neue Zeile hinzufügt.
titaniumdecoy
@titanumdecoy: Ich konnte dieses Verhalten nicht reproduzieren. Welche Version von sed haben Sie verwendet und auf welchem ​​Betriebssystem sind Sie?
David Schmitt
1
@DavidSchmitt: Sie möchten wahrscheinlich sed -rfür erweiterte reguläre Ausdrücke verwenden. Zu diesem Zeitpunkt stimmt das Muster mit dem überein, was in egrep verwendet wird, und Sie können es zur Wiederverwendung in eine Variable einfügen.
Bukzor
Dieser Befehl ersparte mir stundenlanges Kopieren von Header-Dateien aus meiner App für die von mir erstellte Bibliothek. Das ist großartig :) Hier ist der Befehl, den ich egrep -lRZ "\ .h $" verwendet habe. | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;)
The Lazy Coder
11

Ehrlich gesagt, so sehr ich Sed für geeignete Aufgaben liebe, ist dies definitiv eine Aufgabe für Perl - es ist wirklich mächtiger für diese Art von Einzeiler, insbesondere, "es dorthin zurückzuschreiben, wo es herkommt" (Perls -iSchalter erledigt es dafür Sie und optional können Sie auch die alte Version beibehalten, z. B. mit einem angehängten .bak, verwenden Sie -i.bakstattdessen einfach ).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

anstatt komplizierte Arbeit in sed (wenn überhaupt möglich) oder awk ...

Alex Martelli
quelle
6
sed verwendet -i, genau wie Perl.
Stobor
@Stobor - Ich schwöre, ich hatte Probleme, bei denen die Perl-Operation, als ich die Regex-Ersatzzeichenfolge fütterte, genau das tat, was ich wollte, im Gegensatz zu sed, auch wenn ich die Regex-Option gegeben habe sed. Ich glaube, ich habe entweder einige Flags für sed vergessen oder es hatte Einschränkungen.
Meder Omuraliev
10

Ein anderer Weg, dies zu tun

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Hilfe zum obigen Befehl

Die Suche führt die Suche für Sie in dem aktuellen Verzeichnis durch, das durch angegeben ist .

-name Der Name der Datei in meinem Fall, seine pom.xml, kann Platzhalter geben.

-exec ausführen

sed Stream-Editor

-i Fall ignorieren

s ist für Ersatz

/4.6.0.../ Zu durchsuchende Zeichenfolge

/5.0.0.../ Zu ersetzende Zeichenfolge

Cyril Cherian
quelle
vielleicht nicht so mächtig - aber das ist für mich viel einfacher zu verstehen als die akzeptierte Antwort
icc97
5

Ich konnte keinen der Befehle auf dieser Seite für mich verwenden: Die sed-Lösung fügte am Ende aller verarbeiteten Dateien eine neue Zeile hinzu, und die perl-Lösung konnte nicht genügend Argumente von find akzeptieren. Ich habe diese Lösung gefunden, die perfekt funktioniert:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Dadurch wird den aktuellen Verzeichnisbaum und ersetzt rekursiv search_regexmit replacement_stringin beliebigen Dateien mit der Endung .hoder .m.

Ich habe in der Vergangenheit auch rpl für diesen Zweck verwendet.

Titandecoy
quelle
0

Versuchen Sie etwas mit einer for-Schleife

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

nicht schön, sollte aber tun.

Don Johe
quelle
3
xargs ist hier definitiv besser geeignet.
Nathan Fellman
1
und die Verwendung des |while read iMusters würde Streaming ermöglichen und Längenbeschränkungen vermeiden, wenn die Ergebnisse von egrep zu lang werden
David Schmitt
0

Mein Anwendungsfall war ich ersetzen wollte foo:/Drive_Lettermit foo:/bar/baz/xyz konnte ich tun , um es mit dem folgenden Code in meinem Fall. Ich befand mich in demselben Verzeichnis, in dem sich die meisten Dateien befanden.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

hoffe das hat geholfen.

UPDATE s | foo: / Drive_letter: | foo: / ba / baz / xyz | g

neo7
quelle
Sie können andere Begrenzer für den Befehl sed verwenden, und dies macht Pfadnamen viel schöner:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin