Ich schreibe Shell-Skripte für meinen Server, bei dem es sich um ein Shared Hosting handelt, auf dem FreeBSD ausgeführt wird. Ich möchte sie auch lokal auf meinem PC mit Linux testen können. Daher versuche ich, sie portabel zu schreiben, sed
sehe aber keine Möglichkeit, dies zu tun.
Ein Teil meiner Website verwendet generierte statische HTML-Dateien. Diese Sed-Line fügt nach jeder Regeneration den korrekten DOCTYPE ein:
sed -i '1s/^/<!DOCTYPE html> \n/' ${file_name.html}
Es funktioniert mit GNU sed
unter Linux, aber FreeBSD sed
erwartet , dass das erste Argument nach der -i
Option eine Erweiterung für die Sicherungskopie ist. So würde es aussehen:
sed -i '' '1s/^/<!DOCTYPE html> \n/' ${file_name.html}
GNU sed
erwartet jedoch wiederum, dass der Ausdruck unmittelbar danach folgt -i
. (Es sind auch Korrekturen für die Behandlung von Zeilenumbrüchen erforderlich, die hier bereits beantwortet wurden. )
Natürlich kann ich diese Änderung in meine Serverkopie des Skripts aufnehmen, aber das würde z. B. meine Verwendung von VCS für die Versionierung durcheinander bringen. Gibt es eine Möglichkeit, dies mit sed vollständig portabel zu erreichen?
-i
Antworten:
GNU sed akzeptiert eine optionale Erweiterung nach
-i
. Die Erweiterung muss sich im selben Argument befinden und darf kein Leerzeichen enthalten. Diese Syntax funktioniert auch bei BSD sed.Beachten Sie, dass sich unter BSD
-i
auch das Verhalten ändert, wenn mehrere Eingabedateien vorhanden sind: Sie werden unabhängig voneinander verarbeitet (dh sie stimmen z. B.$
mit der letzten Zeile jeder Datei überein). Auch dies funktioniert nicht auf BusyBox.Wenn Sie keine Sicherungsdateien verwenden möchten, können Sie überprüfen, welche Version von sed verfügbar ist.
Alternativ können Sie eine Funktion definieren, um ein Überfrachten der Positionsparameter zu vermeiden.
Wenn Sie sich nicht darum kümmern möchten, verwenden Sie Perl.
Wenn Sie ein portables Skript schreiben möchten, verwenden
-i
Sie es nicht - es ist nicht in POSIX enthalten. Mach manuell, was sed unter der Haube macht - es ist nur noch eine Codezeile.quelle
sed -i
impliziert auch-s
. Und der einfachste Weg, nach einer GNU zu suchen,sed
ist dersed v
Befehl, der für GNU ein gültiger Noop ist, aber an allen anderen Stellen fehlschlägt.sed -i$(sed v < /dev/null 2> /dev/null || echo -n " ''") -e '...' "$file"
Wenn es nicht GNU sed, ein Leerzeichen eingefügt durch zwei Seng Anführungszeichen gefolgt nach ,-i
so dass es arbeitet an BSD. GNU sed bekommt nur-i
.v
Befehl benutze , um auf GNU sed zu testen. Was wäre, wenn FreeBSD sich entschließen würde, es zu implementieren?sed v
vorausgesetzt, das ist der Zweck, zu existieren.Wenn Sie keinen Trick finden, um das
sed
Spiel schön zu machen , können Sie versuchen:Verwenden Sie nicht
-i
:Verwenden Sie Perl
quelle
ed
Sie können immer verwenden
ed
, um einer vorhandenen Datei eine Zeile voran zu stellen.Einzelheiten
Die Bits um das
<!DOCTYPE html>
sind Befehle,ed
um es anzuweisen, diese Zeile der Datei hinzuzufügenmy.html
.sed
Ich glaube, dieser Befehl in
sed
kann auch verwendet werden:quelle
Sie können auch manuell tun, was
perl -i
unter der Haube geschieht:Wie
perl -i
, es gibt keine Sicherung, und wie die meisten Lösungen , die hier gegeben, passen sie die Berechtigungen beeinträchtigen können, das Eigentum an der Datei und kann eine Symlink in eine reguläre Datei drehen.Mit:
sed
würde die Datei über sich selbst überschreiben, so dass Eigentumsrechte und Berechtigungen oder Symlinks nicht beeinträchtigt würden. Es funktioniert mit GNU,sed
dased
normalerweise ein Puffer voller Daten gelesen wurdefile
(in meinem Fall 4 KB), bevor er mit demi
Befehl überschrieben wurde . Das würde nicht funktionieren, wenn die Datei mehr als 4 KB groß wäre, außer dasssed
auch die Ausgabe gepuffert wird.Arbeitet grundsätzlich
sed
mit 4k-Blöcken zum Lesen und Schreiben. Wenn die einzufügende Zeile kleiner als 4 KB ist,sed
wird ein noch nicht gelesener Block niemals überschrieben.Ich würde mich aber nicht darauf verlassen.
quelle
echo '<!DOCTYPE html>'
oder sollte ohne Anführungszeichen "" geflüchtet werden.FreeBSD sed , das auch unter Mac OS X verwendet wird, benötigt die
-e
Option nach dem-i
Wechsel, um den folgenden (regulären) Befehl korrekt und eindeutig zu definieren und zu erkennen.Mit anderen Worten,
sed -i -e ...
sollte mit FreeBSD & GNU funktionierensed
.Im Allgemeinen
sed -i
erfordert das Weglassen der Backup-Erweiterung nach FreeBSD eine explizitesed
Option oder einen Schalter-i
, um Verwirrung auf Seiten von FreeBSDsed
beim Parsen der Befehlszeilenargumente zu vermeiden .(Beachten Sie jedoch, dass die Bearbeitung von Dateien an Ort und
sed
Stelle zu Änderungen des Dateieingangs führt, siehe Bearbeiten von Dateien an Ort und Stelle. )(Generell haben neuere Versionen von FreeBSD
sed
den-r
Schalter, um die Kompatibilität mit GNU zu erhöhensed
).quelle
bsdsed -i -e 's/a/A/'
erfolgt nicht direkt, sondern durch Speichern des Originals mit dem Suffix "-e" (testfile.txt-e
).Um
sed -i
eine einzelne Datei portabel zu emulieren und dabei die Rennbedingungen so weit wie möglich zu vermeiden:Übrigens wird hiermit auch das mögliche Problem
sed -i
behoben, das je nach Verzeichnis- und Dateiberechtigungen dazu führen kann,sed -i
dass ein Benutzer eine Datei überschreibt, die dieser Benutzer nicht bearbeiten darf.Sie könnten auch Backups machen wie:
quelle
Sie können Vim im Ex-Modus verwenden:
1
erste Zeile auswähleni
Text und Zeilenumbruch einfügenx
speichern und schließenOder auch, wie in den Kommentaren, einfach alter Standard
ex
:quelle
ex
, mit der Ausnahme, dass keine Implementierungen erforderlich sind , um mehrere-c
Flags zu unterstützen . Für bestimmte Portabilität würde ichprintf '%s\n' 1i '<!DOCTYPE html>' . x | ex file