Das Ersetzen von Zeichenfolgen in Dateien basierend auf bestimmten Suchkriterien ist eine sehr häufige Aufgabe. Wie kann ich
- Ersetzen Sie die Zeichenfolge
foomitbarin allen Dateien im aktuellen Verzeichnis? - dasselbe rekursiv für Unterverzeichnisse tun?
- nur ersetzen, wenn der Dateiname mit einer anderen Zeichenfolge übereinstimmt?
- nur ersetzen, wenn der String in einem bestimmten Kontext gefunden wird?
- Ersetzen, wenn der String auf einer bestimmten Zeilennummer steht?
- mehrere Zeichenfolgen durch denselben Ersatz ersetzen
- mehrere Zeichenfolgen durch unterschiedliche Ersetzungen ersetzen
Akzeptierte Antwort:
1. Ersetzen aller Vorkommen einer Zeichenfolge durch eine andere in allen Dateien im aktuellen Verzeichnis:
Diese sind für Fälle, in denen Sie wissen dass das Verzeichnis nur reguläre Dateien enthält und Sie alle nicht versteckten Dateien verarbeiten möchten. Wenn dies nicht der Fall ist, verwenden Sie die Ansätze in 2.
Alle sed Lösungen in dieser Antwort setzen GNU sed voraus . Wenn Sie FreeBSD oder macOS verwenden, ersetzen Sie -i mit -i '' . Beachten Sie auch, dass die Verwendung des -i wechseln Sie mit einer beliebigen Version von sed hat bestimmte Auswirkungen auf die Dateisystemsicherheit und ist in keinem Skript ratsam, das Sie in irgendeiner Weise verteilen möchten.
-
Nicht rekursiv, Dateien nur in diesem Verzeichnis:
sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./*
(die perl einer schlägt fehl für Dateinamen, die mit | enden oder Leerzeichen)).
-
Rekursive, reguläre Dateien (einschließlich versteckter ) in diesem und allen Unterverzeichnissen
find . -type f -exec sed -i 's/foo/bar/g' {} +Wenn Sie zsh verwenden:
sed -i -- 's/foo/bar/g' **/*(D.)(kann fehlschlagen, wenn die Liste zu groß ist, siehe
zargszu umgehen).Bash kann nicht direkt nach regulären Dateien suchen, eine Schleife ist erforderlich (geschweifte Klammern vermeiden das globale Setzen der Optionen):
( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done )Die Dateien werden ausgewählt, wenn sie tatsächliche Dateien sind (-f) und beschreibbar sind (-w).
2. Nur ersetzen, wenn der Dateiname mit einer anderen Zeichenfolge übereinstimmt / eine bestimmte Erweiterung hat / von einem bestimmten Typ ist usw.:
-
Nicht rekursiv, Dateien nur in diesem Verzeichnis:
sed -i -- 's/foo/bar/g' *baz* ## all files whose name contains baz sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz -
Rekursive, reguläre Dateien in diesem und allen Unterverzeichnissen
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +Wenn Sie Bash verwenden (geschweifte Klammern vermeiden das globale Setzen der Optionen):
( shopt -s globstar dotglob sed -i -- 's/foo/bar/g' **baz* sed -i -- 's/foo/bar/g' **.baz )Wenn Sie zsh verwenden:
sed -i -- 's/foo/bar/g' **/*baz*(D.) sed -i -- 's/foo/bar/g' **/*.baz(D.)
Der -- dient dazu, sed mitzuteilen dass keine Flags mehr in der Kommandozeile angegeben werden. Dies ist nützlich, um Dateinamen zu schützen, die mit - beginnen .
-
Ist eine Datei von einem bestimmten Typ, beispielsweise ausführbar (siehe
man findfür weitere Optionen):find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
zsh :
sed -i -- 's/foo/bar/g' **/*(D*)
3. Nur ersetzen, wenn der String in einem bestimmten Kontext gefunden wird
-
Ersetzen Sie
foomitbarnur wenn einbazvorhanden ist später in derselben Zeile:sed -i 's/foo(.*baz)/bar1/' file
In sed , mit ( ) speichert, was in Klammern steht, und Sie können dann mit 1 darauf zugreifen . Es gibt viele Variationen dieses Themas, um mehr über solche regulären Ausdrücke zu erfahren, siehe hier.
-
Ersetzen Sie
foomitbarnur wennfoobefindet sich in der 3D-Spalte (Feld) der Eingabedatei (unter der Annahme von durch Leerzeichen getrennten Feldern):gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
(benötigt gawk 4.1.0 oder neuer).
-
Verwenden Sie für ein anderes Feld einfach
$NwobeiNist die Nummer des Interessengebiets. Für ein anderes Feldtrennzeichen (:in diesem Beispiel) verwenden Sie:gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
Eine andere Lösung mit perl :
perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo
HINWEIS:Sowohl das awk und perl Lösungen wirken sich auf die Abstände in der Datei aus (entfernen Sie die führenden und abschließenden Leerzeichen und wandeln Sie in den übereinstimmenden Zeilen Sequenzen von Leerzeichen in ein Leerzeichen um). Verwenden Sie für ein anderes Feld $F[N-1] wobei N die gewünschte Feldnummer ist und für ein anderes Feldtrennzeichen verwendet wird (der $"=":" setzt das Ausgabefeldtrennzeichen auf : ):
perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo
-
Ersetzen Sie
foomitbarnur in der 4. Zeile:sed -i '4s/foo/bar/g' file gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file perl -i -pe 's/foo/bar/g if $.==4' file
4. Mehrere Ersetzungsoperationen:Ersetzen durch verschiedene Zeichenfolgen
-
Sie können
sedkombinieren Befehle:sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
Beachten Sie, dass die Reihenfolge wichtig ist (sed 's/foo/bar/g; s/bar/baz/g' ersetzt foo mit baz ).
-
oder Perl-Befehle
perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file -
Wenn Sie eine große Anzahl von Mustern haben, ist es einfacher, Ihre Muster und ihre Ersetzungen in einem
sedzu speichern Skriptdatei:#! /usr/bin/sed -f s/foo/bar/g s/baz/zab/g -
Oder, wenn Sie zu viele Musterpaare haben, als dass das obige durchführbar wäre, können Sie Musterpaare aus einer Datei lesen (zwei durch Leerzeichen getrennte Muster, $Muster und $Ersetzung, pro Zeile):
while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt -
Das wird bei langen Listen von Mustern und großen Datendateien ziemlich langsam sein, also möchten Sie vielleicht die Muster lesen und einen
sederstellen Skript von ihnen stattdessen. Im Folgenden wird von einem <Leerzeichen> ausgegangen Trennzeichen trennt eine Liste von MATCH<Leerzeichen>REPLACE Paare, die in der Dateipatterns.txteinzeln pro Zeile vorkommen :sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt | sed -f- ./editfile >outfile
Das obige Format ist weitgehend willkürlich und lässt beispielsweise kein <Leerzeichen> zu in einem von MATCH oder ERSETZEN . Die Methode ist jedoch sehr allgemein:im Grunde, wenn Sie einen Ausgabestrom erstellen können, der wie ein sed aussieht script, dann können Sie diesen Stream als sed beziehen Skript durch Angabe von sed ’s-Skriptdatei als - stdin.
-
Sie können mehrere Skripte auf ähnliche Weise kombinieren und verketten:
SOME_PIPELINE | sed -e'#some expression script' -f./script_file -f- -e'#more inline expressions' ./actual_edit_file >./outfile
Ein POSIX sed verkettet alle Skripte in der Reihenfolge, in der sie auf der Befehlszeile erscheinen, zu einem. Keines davon muss auf ein n enden ewline.
-
grepkann genauso funktionieren:sed -e'#generate a pattern list' <in | grep -f- ./grepped_file -
Wenn Sie mit festen Zeichenfolgen als Muster arbeiten, empfiehlt es sich, Metazeichen regulärer Ausdrücke mit Escapezeichen zu versehen . Das geht ganz einfach:
sed 's/[]$&^*./[]/\&/g s| *([^ ]*) *([^ ]*).*|s/1/2/g| ' <patterns.txt | sed -f- ./editfile >outfile
5. Mehrere Ersetzungsoperationen:Ersetzen Sie mehrere Muster durch dieselbe Zeichenfolge
-
Ersetzen Sie
foo,baroderbazmitfoobarsed -Ei 's/foo|bar|baz/foobar/g' file -
oder
perl -i -pe 's/foo|bar|baz/foobar/g' file