Gibt es eine Alternative zum Befehl "sed -i" in Solaris?

11

Ich habe in meinem Projekt die Anforderung, vorhandenen Text in einer Datei foodurch einen anderen Text wie den fooofoofolgenden zu ersetzen :

abc.txt
name
foo
foo1

Also habe ich versucht:

sed -i "s/foo/fooofoo/g" abc.txt

Ich erhalte jedoch diesen Fehler:

sed: illegale Option - i

Ich habe in dem Handbuch gefunden, dass ich verwenden muss:

sed -i\ "s/foo/fooofoo/g" abc.txt

Dies funktioniert jedoch auch nicht.

Ich habe Alternativen in perlund awkauch gefunden, aber eine Lösung in Solaris sedwäre sehr dankbar.

Ich benutze diese Version von Bash:

GNU Bash, Version 3.2.57 (1) -Veröffentlichung (sparc-sun-solaris2.10)

tpsaitwal
quelle
Verwenden Sie unter Solaris 11 und höher einfach /usr/gnu/bin/sed-i, um Unterstützung zu erhalten.
Alanc

Antworten:

15

Verwenden Sie ed. Es ist auf den meisten Plattformen verfügbar und kann Ihre Dateien direkt bearbeiten.
Da sedbasiert auf edder Syntax zum Ersetzen von Mustern ist ähnlich:

ed -s infile <<\IN
,s/old/new/g
w
q
IN
don_crissti
quelle
Warum edstatt exnur neugierig? (Ich weiß, dass beide POSIX-kompatibel sind und ich habe mit den Spezifikationen verlinkt .;))
Wildcard
1
@ Wildcard - ich könnte das Gegenteil fragen - warum ex? Um Ihre Frage zu beantworten: Da ich nie benutze, ex bin ich nicht damit vertraut. Übrigens habe ich Setups gesehen, bei denen edvorhanden war, aber exnicht (aber nicht das Gegenteil) ... das bemerkenswerteste ist mein Laptop :)
don_crissti
Gute Antwort. :) Meine Antwort: Da ich ständig Vim benutze, bin ich mit exBefehlen sehr vertraut . Alle viBefehle, die Sie eingeben können und die mit einem Doppelpunkt beginnen, sind exBefehle. Es gibt natürlich einige Vim-spezifische Erweiterungen, aber nur der Kernsatz von Befehlen ist unglaublich leistungsfähig und flexibel und bietet ausreichend Platz für Skriptbearbeitungen.
Wildcard
Mein Manjaro-System hat exund doch nicht ed.
Mitte
8

Wenn Sie GNU sed nicht installieren können, verwenden Sie:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Dies verwendet die Umleitung , um die Ausgabe von sed an eine temporäre Datei zu senden. Wenn sed erfolgreich abgeschlossen wurde, wird dies abc.txtmit der temporären Datei überschrieben .

Wie aus dem Quellcode für GNU sed hervorgeht , ist genau dies der sed -iFall. Das ist also ungefähr so ​​effizient wie sed -i.

Wenn eine Möglichkeit abc.tmpbesteht, die bereits besteht, möchten Sie möglicherweise mktempein ähnliches Dienstprogramm verwenden, um den eindeutigen Namen für die temporäre Datei zu generieren.

John1024
quelle
Danke für die Hilfe. Gibt es in Solaris jedoch eine Option, mit der wir die vorhandene Datei aktualisieren können, ohne eine temporäre Datei zu erstellen?
Tpsaitwal
10
Ich hasse es, es dir zu brechen, aber selbst sed erstellt eine temporäre Datei. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller
1
Sie können es tun, wenn Sie sich nicht für harte Links interessieren - siehe mein Beispiel. Es gibt noch eine temporäre Datei, aber sie ist versteckt (unbenannt). Ohne eine temporäre, müssten Sie verwenden ed, da es die gesamte Datei in den Speicher liest.
Toby Speight
3

Wenn Sie das Äquivalent von wollen sed -i.bak, ist es ziemlich einfach.

Betrachten Sie dieses Skript für GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Wir können einfach die markierte Linie durch ersetzen

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Dadurch wird die vorhandene Datei als Backup verschoben und eine neue Datei geschrieben.

Wenn wir das Äquivalent von wollen

sed -i -e "$script" "$dir"  # no backup

dann ist es etwas komplexer. Wir können die Datei zum Lesen als Standardeingabe öffnen und dann die Verknüpfung aufheben, bevor wir die Ausgabe von sed anweisen, sie zu ersetzen:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Wir machen das in einer Sub-Shell, so dass unser ursprünglicher Standard danach noch verfügbar ist. Es ist möglich, Eingänge zu schalten und ohne Unterschale zurückzuschalten, aber dieser Weg scheint mir klarer zu sein.

Beachten Sie, dass wir zuerst sorgfältig kopieren, anstatt eine neue fooDatei zu erstellen. Dies ist wichtig, wenn die Datei unter mehr als einem Namen bekannt ist (dh über feste Links verfügt) und Sie sicherstellen möchten, dass Sie die Links nicht unterbrechen .

Toby Speight
quelle
3

Zunächst sollten Sie wissen, dass der i\Befehl , auf den Sie sich beziehen, zum Einfügen einer Textzeile dient und nicht zum Zurückspeichern des bearbeiteten Textes in die Datei. Es gibt keine POSIX-spezifizierte Möglichkeit, dies zu verwenden sed.

Was Sie können tun , ist Einsatz ex, die sich durch POSIX spezifiziert :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

Der xBefehl lautet "Speichern und beenden". Sie können auch verwenden wq, xist aber kürzer.

Das %Zeichen am Anfang des Ersatzbefehls bedeutet: "Wenden Sie diesen Befehl auf jede Zeile im Puffer an." Sie könnten 1,$s/find/replace/gauch verwenden.


Ein wesentlicher Unterschied zwischen exund sedist, dass sedes sich um einen Stream- Editor handelt. Es arbeitet nur sequentiell, Zeile für Zeile. exist weitaus flexibler als das, und in der Tat können Sie interaktive Textdateien direkt in bearbeiten ex. Es ist der unmittelbare Vorgänger von vi.

Platzhalter
quelle
1

Verwenden sedund keine sichtbare temporäre Datei:

Sie können vermeiden, eine separate sichtbare "temporäre Datei" zu erstellen:

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Erläuterung

Unix-ähnliche Systeme nicht tatsächlich die Inhalte von der Festplatte der Datei entfernen , bis es beide nicht verknüpfte in dem Dateisystem, und nicht in jedem Prozess zu öffnen. Sie können also exec 3<die Datei in der Shell öffnen, um sie in Dateideskriptor 3 zu lesen, rmder Datei (die sie vom Dateisystem trennt), und dann sedmit Dateideskriptor 3 aufrufen, der als Eingabe verwendet wird.

Beachten Sie, dass dies sehr unterschiedlich ist:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

Der Unterschied besteht darin, dass die Shell bei einem Befehl nur dieselbe Datei zum Lesen und zum Schreiben mit der Option zum Abschneiden der Datei öffnet. Da es sich immer noch um dieselbe Datei handelt, verlieren Sie den Inhalt. Wenn Sie es jedoch zum Lesen rmöffnen und dann denselben Pfadnamen zum Schreiben öffnen, erstellen Sie tatsächlich eine neue Datei mit demselben Pfadnamen (jedoch an einem neuen Inode- und Festplattenspeicherort, da das Original noch geöffnet ist): Also Der Inhalt ist noch verfügbar.

Sobald Sie fertig sind, können Sie den zuvor geöffneten Dateideskriptor schließen (dies ist die exec 3<&-spezielle Syntax), wodurch die Originaldatei freigegeben wird, sodass das Betriebssystem den Speicherplatz löschen (als nicht verwendet markieren) kann.

Vorsichtsmaßnahmen

Bei dieser Lösung sind einige Dinge zu beachten:

  1. Sie erhalten nur ein "Durchgehen" des Inhalts - es gibt keine tragbare Möglichkeit für eine Shell, im Dateideskriptor "zurückzusuchen". Sobald ein Programm einen Teil des Inhalts liest, sehen andere Programme nur den Rest der Datei. Und sedliest die gesamte Datei.

  2. Es besteht eine geringe Wahrscheinlichkeit, dass Ihre Originaldatei verloren geht, wenn Ihre Shell / Ihr Skript / Ihre Sed getötet wird, bevor sie fertig ist.

mtraceur
quelle
Um es klar auszudrücken, @TobySpeight hat bereits am Ende seiner Antwort ein Beispiel für diese Lösung veröffentlicht, aber ich dachte, es könnte eine eingehendere Ausarbeitung gebrauchen.
mtraceur
An den Downvoter: Ich bin mir sicher, dass Sie sich gut fühlen, wenn Sie nur ein Downvote hinterlassen. Ich würde mich freuen, wenn Sie mehr dazu beitragen würden, indem Sie Feedback geben, welche Probleme Sie mit dieser Antwort haben, wie sie verbessert werden kann usw. .
mtraceur
Vielleicht können Sie perl -i
c4f4t0r
@ c4f4t0r: Entschuldigung für die späte Antwort: Ja, perl -iist eine weitere gute Option. Die Frage verlangte speziell eine Lösung, die mit Solaris kompatibel ist sed, im Gegensatz zu Lösungen mit perlund awkdie sie bereits erwähnt haben. Außerdem sollte meine Antwort hauptsächlich etwas hinzufügen, das ich für nützlich hielt, um es zu wissen und in keiner der anderen Antworten zu vermissen.
mtraceur