it-swarm.com.de

Der Befehl sed mit der Option -i (direkte Bearbeitung) funktioniert unter Ubuntu, aber nicht unter Mac

Ich weiß nichts über Sed), benötige aber diesen Befehl (der unter Ubuntu gut funktioniert), um auf einem Mac OSX zu funktionieren:

sed -i "/ $domain .*#drupalpro/d" /etc/hosts

Ich erhalte:

sed: 1: "/etc/hosts": extra characters at the end of h command
89

Ubuntu wird mit GNU sed ausgeliefert, wobei das Suffix für das -i Option ist optional. OS X wird mit BSD sed ausgeliefert, wobei das Suffix obligatorisch ist. Versuchen sed -i ''

145
microtherion

Zur Ergänzung die hilfreiche, auf den Punkt gebrachte Antwort von microtherion :

  • mit einer tragbaren Lösung
  • mit Hintergrundinformationen

tl; dr :

Das Äquivalent dazu [~ # ~] gnu [~ # ~]sed (Standard Auf den meisten Linux Distributionen:

sed -i    's/foo/bar/' file

ist das BSD/macOSsed Kommando:

sed -i '' 's/foo/bar/' file  # Note the '' as a *separate argument*

Mit BSD/macOSsed können die folgenden Befehle mach nicht arbeite überhaupt oder nicht wie beabsichtigt:

sed -i    's/foo/bar/' file  # Breaks; script is misinterpreted as backup-file suffix
sed -i''  's/foo/bar/' file  # Ditto
sed -i -e 's/foo/bar/' file  # -e is misinterpreted as backup-file suffix

Für eine Diskussion von all den Unterschieden zwischen GNU sed und BSD/macOS sed siehe this Antwort von mir.

Portabler Ansatz :

Hinweis: Portable hier bedeutet, dass der Befehl mit beiden beschriebenen Implementierungen funktioniert. Es ist nicht portierbar in einem [~ # ~] posix [~ # ~] Sinn, weil die Option -i Ist nicht POSIX-kompatibel .

# Works with both GNU and BSD/macOS Sed, due to a *non-empty* option-argument:
# Create a backup file *temporarily* and remove it on success.
sed -i.bak 's/foo/bar/' file && rm file.bak

Für eine Erklärung siehe unten; Alternative Lösungen, einschließlich POSIX-kompatibler Lösungen, finden Sie unter diese Antwort von mir.


Hintergrundinformation

In [~ # ~] gnu [~ # ~]sed (Standard bei den meisten Linux-Distributionen) und BSD/macOSsed, die Option -i, die direkte Aktualisierung ausführt[1] Akzeptiert ein Optionsargument, das angibt, welches Suffix (Dateinamenerweiterung) für die Sicherungsdatei verwendet werden soll = der zu aktualisierenden Datei .

Beispielsweise wird in both -Implementierungen die ursprüngliche Datei file als Sicherungsdatei file.bak Beibehalten:

sed -i.bak 's/foo/bar/' file  # Keep original as 'file.bak'; NO SPACE between -i and .bak

Obwohl mit [~ # ~] gnu [~ # ~]sed das Suffix-Argument optional ist Während bei BSD/macOSsed obligatorisch die obige Syntax mit beide Implementierungen, da direkt neben dem Optionsargument (.bak) die Option (-i) - -i.bak im Gegensatz zu -i .bak - funktioniert sowohl als optional als auch als obligatorisch option-argument :

  • Syntax -i.bak Ist das only Formular, das für ein optional Optionsargument funktioniert.
  • Syntax -i.bak also funktioniert als obligatorisch Optionsargument, als Alternative zu -i .bak, Dh Angabe der Option und ihres Arguments separat.

Das Fehlen eines Suffixes - was häufig der Fall ist - bedeutet, dass keine Sicherungsdatei vorhanden ist sollte beibehalten werden, und an dieser Stelle tritt die Inkompatibilität auf :

  • Wenn Sie [~ # ~] gnu [~ # ~]sed nicht angeben, verwenden Sie einfach -i von selbst.

  • Bei BSD/macOSsed bedeutet das Fehlen eines Suffix, dass der leere String als - obligatorisches - Suffix angegeben wird, und aus technisch Gründen kann der leere String nur als getrennt Argument übergeben werden: das heißt , -i '' nicht-i''.

-i'' Funktioniert nicht, weil es für sed nicht von nur -i zu unterscheiden ist, weil die Shell effektiv entfernt die leeren Anführungszeichen (es verkettet -i und '' und entfernt Anführungszeichen mit syntaktischer Funktion) und Übergibt nur -i in beiden Fällen.

Wenn (effektiv) nur -i Angegeben ist, wird das Argument next als Optionsargument interpretiert:

sed -i 's/foo/bar/' file # BREAKS with BSD/macOS Sed

's/foo/bar/' - bestimmt für das Sed Skript (Befehl) - wird jetzt als Suffix interpretiert , und das Wort file wird als Skript interpretiert.
Das Interpretieren eines solchen Wortes als Skript führt dann zu einer undurchsichtigen Fehlermeldung wie
sed: 1: "file": invalid command code f,
weil das f als ein Sed Befehl (Funktion) interpretiert wird.

Ebenso mit:

sed -i -e 's/foo/bar/' file # CREATES BACKUP FILE 'file-e'

-e Wird als das Suffix Argument interpretiert und NICHT als Seds -e Option (mit dem bei Bedarf mehrere Befehle angegeben werden können).
Als Ergebnis erhalten Sie anstatt KEINE Sicherung zu behalten, eine Sicherungsdatei mit dem Suffix -e.

Dass dieser Befehl nicht wie beabsichtigt funktioniert, ist weniger offensichtlich, da die direkte Aktualisierung erfolgreich ist, da die Syntaxanforderung des Suffix-Arguments durch das Argument -e Erfüllt wird.

Dass die versehentliche Erstellung dieser Sicherungsdateien leicht unbemerkt bleibt, ist die wahrscheinlichste Erklärung dafür, dass Crts falsche Antwort und diese falsche Antwort auf eine ähnliche Frage so viele Gegenstimmen erhalten haben (wie dieses Schreibens).


[1] Genau genommen wird eine temporäre Datei hinter den Kulissen erstellt, die dann ersetzt die ursprüngliche Datei; Dieser Ansatz kann problematisch sein: siehe die untere Hälfte von diese Antwort von mir.

72
mklement0

mann ist dein Freund.

OS X

 -i extension
         Edit files in-place, saving backups with the specified extension.
         If a zero-length extension is given, no backup will be saved.  It
         is not recommended to give a zero-length extension when in-place
         editing files, as you risk corruption or partial content in situ-
         ations where disk space is exhausted, etc.
21

Unter OS X können Sie die GNU Version von sed: gsed verwenden.

# if using brew
brew install gnu-sed

#if using ports
Sudo port install gsed

Wenn Ihr Skript dann portierbar sein soll, können Sie je nach Betriebssystem festlegen, welcher Befehl verwendet werden soll.

SED=sed
unamestr=`uname`
if [[ "$unamestr" == "Darwin" ]] ; then
    SED=gsed
    type $SED >/dev/null 2>&1 || {
        echo >&2 "$SED it's not installed. Try: brew install gnu-sed" ;
        exit 1;
    }
fi
# here your sed command, e.g.:
$SED -i "/ $domain .*#drupalpro/d" /etc/hosts
5
jcarballo