Suchen und Ersetzen in einer Datei und Überschreiben der Datei funktioniert nicht, es wird die Datei geleert

604

Ich möchte ein Suchen und Ersetzen in einer HTML-Datei über die Befehlszeile ausführen.

Mein Befehl sieht ungefähr so ​​aus:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Wenn ich dies ausführe und die Datei anschließend betrachte, ist sie leer. Es hat den Inhalt meiner Datei gelöscht.

Wenn ich dies nach dem erneuten Wiederherstellen der Datei ausführe:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Das stdoutist der Inhalt der Datei, und das Suchen und Ersetzen wurde ausgeführt.

Warum passiert dies?

BBales
quelle
13
Perl Alternative:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
viel verwandter sedBefehl, um eine Zeichenfolge zu finden und die gesamte Zeile zu ersetzen: stackoverflow.com/questions/11245144/…
cregox

Antworten:

917

Wenn die Shell> index.html in der Befehlszeile sieht , öffnet sie die Datei index.htmlzum Schreiben und löscht alle vorherigen Inhalte.

Um dies zu beheben, müssen Sie die -iOption übergeben, sedum die Änderungen inline vorzunehmen und eine Sicherungskopie der Originaldatei zu erstellen, bevor die Änderungen direkt vorgenommen werden:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Ohne die .bak schlägt der Befehl auf einigen Plattformen wie Mac OSX fehl.

Codaddict
quelle
20
Sagen truncates the filestatt opens the filewahrscheinlich macht es klarer.
Mikel
12
Zumindest auf meinem Mac funktioniert der erste Vorschlag nicht. Wenn Sie eine Datei direkt ersetzen, müssen Sie eine Erweiterung angeben. Sie können jedoch zumindest eine Erweiterung mit der Länge Null übergeben: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza
5
für Variablen sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Fatima Zohra
33
Verwenden Sie unter osx eine leere Zeichenfolge '' als Parameter für -i, z. sed -i '' 's/blah/xx/g'
Pierre Houston
4
aber was ist deins .bakdanach sed -i?
Patrizio Bertoni
210

Ein alternatives, nützliches Muster ist:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

-iDies hat fast den gleichen Effekt, ohne die Option zu verwenden, und bedeutet außerdem, dass die Eingabedatei nicht überlastet wird, wenn das sed-Skript aus irgendeinem Grund fehlschlägt. Wenn die Bearbeitung erfolgreich ist, liegt keine Sicherungsdatei mehr herum. Diese Art von Redewendung kann in Makefiles nützlich sein.

Sehr viele Samen haben die -iOption, aber nicht alle; Das Posix Sed ist eines, das dies nicht tut. Wenn Sie Portabilität anstreben, sollten Sie dies am besten vermeiden.

Norman Gray
quelle
9
+1 für keine herumliegende Sicherungsdatei und keine überlastete Eingabedatei, wenn die Bearbeitung fehlschlägt. Funktionierte einwandfrei auf dem Mac.
Mike Grace
Hat perfekt für mich funktioniert. Vielen Dank! (auf einem Mac)
interessiert
1
Dies funktionierte perfekt für mich, wo auf Ubuntu Server 14.04 sed -i die Datei immer wieder auf Null stellte.
Chris Giddings
2
Extrem geringfügige Verbesserung:... && mv index.html{.tmp,}
EdwardGarson
5
@EdwardGarson In der Tat würde ich das wahrscheinlich verwenden, wenn ich es tippe - ich stimme zu, dass es ordentlicher ist - aber sh(wenn ich mich richtig erinnere) hat diese {...}Erweiterung nicht. In einem Makefile verwenden Sie möglicherweise sheher als bash. Wenn Sie also Portabilität (oder Posixität) anstreben, müssen Sie diese Konstruktion vermeiden.
Norman Gray
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Dies führt eine globale direkte Ersetzung in der Datei index.html durch. Das Zitieren der Zeichenfolge verhindert Probleme mit Leerzeichen bei der Abfrage und beim Ersetzen.

Reiches Apodaca
quelle
57

Verwenden Sie die Option -i von sed, z

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
quelle
Was bedeutet das? sed: -i darf nicht mit stdin verwendet werden
Blatt
2
Denken Sie daran, Ihr Muster in Anführungszeichen zu setzen, wenn es Leerzeichen enthält -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson
@sheetal: Führt -idie direkte Bearbeitung von Dateien durch , daher ist es nicht sinnvoll, sie mit stdin- Eingaben zu kombinieren .
mklement0
Dies funktioniert möglicherweise unter macOS, aber unter Arch Linux für mich nicht.
xdevs23
Ohne das -e funktioniert die akzeptierte Antwort unter MacOS, Catalina, nicht. Mit dem -e funktioniert es.
Cwhiii
18

So ändern Sie mehrere Dateien (und speichern jeweils ein Backup als * .bak):

perl -p -i -e "s/\|/x/g" *  

nehmen Sie alle Dateien im Verzeichnis und ersetzen |mit x dieser eine „Perl pie“ (einfach wie eine Torte) genannt

Stenemo
quelle
1
Gut zu sehen, dass jemand bereit ist, sich die Problemstellung und nicht nur die Tags anzusehen. OP hat nicht sedals Anforderung angegeben, sondern nur als bereits ausprobiertes Tool verwendet.
user7412956
14

Sie sollten versuchen, die Option -ifür die direkte Bearbeitung zu verwenden.

uloBasEI
quelle
6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Wenn Sie einen Link hinzufügen möchten, versuchen Sie dies. Suchen Sie wie oben nach der URL (beginnend mit https und endend mit.com hier) und ersetzen Sie sie durch eine URL-Zeichenfolge. Ich habe $pub_urlhier eine Variable verwendet . sbedeutet hier Suche und gbedeutet globalen Ersatz.

Es klappt !

Kaey
quelle
6

Achtung: Dies ist eine gefährliche Methode! Es missbraucht die E / A-Puffer unter Linux und schafft es mit bestimmten Pufferoptionen, mit kleinen Dateien zu arbeiten. Es ist eine interessante Neugier. Aber verwenden Sie es nicht für eine reale Situation!

Neben der -iOption sed können Sie das teeDienstprogramm verwenden .

Von man:

Tee - Lesen Sie von der Standardeingabe und schreiben Sie in die Standardausgabe und -dateien

Die Lösung wäre also:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- Hier wird das teewiederholt, um sicherzustellen, dass die Pipeline gepuffert ist. Dann werden alle Befehle in der Pipeline blockiert, bis sie Eingaben zur Bearbeitung erhalten. Jeder Befehl in der Pipeline startet, wenn die Upstream-Befehle 1 Byte-Puffer (die Größe ist irgendwo definiert ) in die Eingabe des Befehls geschrieben haben. Der letzte Befehl tee index.html, der die Datei zum Schreiben öffnet und daher leert, wird ausgeführt, nachdem die Upstream-Pipeline beendet wurde und sich die Ausgabe im Puffer innerhalb der Pipeline befindet.

Höchstwahrscheinlich funktioniert Folgendes nicht:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- Es werden beide Befehle der Pipeline gleichzeitig ausgeführt, ohne dass sie blockiert werden. (Ohne Blockierung sollte die Pipeline die Bytes Zeile für Zeile anstelle von Puffer für Puffer passieren. Wie beim Ausführen cat | sed s/bar/GGG/. Ohne Blockierung ist sie interaktiver und normalerweise werden Pipelines mit nur 2 Befehlen ohne Pufferung und Blockierung ausgeführt. Längere Pipelines werden gepuffert.) Der tee index.htmlWille Öffnen Sie die Datei zum Schreiben und sie wird geleert. Wenn Sie die Pufferung jedoch immer aktivieren, funktioniert auch die zweite Version.

xealits
quelle
3
Die Ausgabedatei von tee wird ebenfalls sofort geöffnet, was zu einer leeren index.html für den gesamten Befehl führt.
sjngm
3
Dies wird korrupt jede Eingabedatei , die größer ist als die Rohrleitungspuffer (der typischerweise 64 KB) . (@sjngm: Die Datei wird nicht wie bei sofort abgeschnitten >, aber der Punkt ist, dass es sich um eine fehlerhafte Lösung handelt, die wahrscheinlich zu Datenverlust führt.)
mklement0
4

Das Problem mit dem Befehl

sed 'code' file > file

ist, dass filees von der Shell abgeschnitten wird, bevor sed es tatsächlich verarbeiten kann. Als Ergebnis erhalten Sie eine leere Datei.

Die beste Möglichkeit, dies zu tun, besteht darin -i, die Bearbeitung an Ort und Stelle durchzuführen, wie in anderen Antworten vorgeschlagen. Dies ist jedoch nicht immer das, was Sie wollen. -ierstellt eine temporäre Datei, die dann zum Ersetzen der Originaldatei verwendet wird. Dies ist problematisch, wenn Ihre Originaldatei ein Link war (der Link wird durch eine reguläre Datei ersetzt). Wenn Sie Links beibehalten müssen, können Sie die Ausgabe von sed mithilfe einer temporären Variablen speichern, bevor Sie sie wie folgt in die Datei zurückschreiben:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Besser noch, verwenden Sie printfanstelle von echoda echowird wahrscheinlich \\wie \in einigen Shells (z. B. Strich) verarbeitet:

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Andrzej Pronobis
quelle
1
+1 zum Erhalt von Links. Es funktioniert auch mit einer temporären Datei:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
Dashohoxha
3

Und die edAntwort:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Um es zu wiederholen , was codaddict beantwortet , die Schalengriffe die Umleitung erste , die „input.html“ Datei auszulöschen, und dann ruft die Shell den „sed“ -Befehl es nun leere Datei übergeben.

Glenn Jackman
quelle
2
kurze Frage, warum die Leute immer wieder "die edVersion" der sedAntworten geben? funktioniert es schneller?
Cregox
6
Einige seds implementieren nicht, -ium direkt zu bearbeiten. edist allgegenwärtig und ermöglicht es Ihnen, Ihre Änderungen in der Originaldatei zu speichern. Außerdem ist es immer gut, viele Werkzeuge in Ihrem Kit zu haben.
Glenn Jackman
OK Cool. In Bezug auf die Leistung sind sie also wahrscheinlich die gleichen. Vielen Dank!
Cregox
2

Sie können Vim im Ex-Modus verwenden:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % Wählen Sie alle Zeilen aus

  2. x speichern und schließen

Steven Penny
quelle
0

Ich habe nach der Option gesucht, mit der ich den Linienbereich definieren und die Antwort finden kann. Zum Beispiel möchte ich Host1 von Zeile 36-57 in Host2 ändern.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Sie können auch die Option gi verwenden, um die Groß- und Kleinschreibung zu ignorieren.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

quelle
0

Bei allem Respekt vor den oben genannten richtigen Antworten ist es immer eine gute Idee, solche Skripte "trocken laufen" zu lassen, damit Sie Ihre Datei nicht beschädigen und von vorne beginnen müssen.

Lassen Sie Ihr Skript einfach die Ausgabe in die Befehlszeile übertragen, anstatt sie beispielsweise so in die Datei zu schreiben:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

ODER

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

Auf diese Weise können Sie die Ausgabe des Befehls anzeigen und überprüfen, ohne dass Ihre Datei abgeschnitten wird.

Nestor Milyaev
quelle