it-swarm.com.de

sed In-Place-Flag, das sowohl unter Mac (BSD) als auch unter Linux funktioniert

Gibt es einen Aufruf von sed, um die direkte Bearbeitung ohne Backups durchzuführen, der sowohl unter Linux als auch unter Mac funktioniert? Während das mit OS X ausgelieferte BSD sedsed -i '' …, die Linux-Distribution GNU sed interpretiert die Anführungszeichen normalerweise als leeren Eingabedateinamen (anstelle der Backup-Erweiterung) und benötigt sed -i … stattdessen.

Gibt es eine Befehlszeilensyntax, die mit beiden Varianten funktioniert, sodass ich auf beiden Systemen dasselbe Skript verwenden kann?

204
klickverbot

Wenn Sie wirklich einfach nur sed -i Auf die 'einfache' Weise verwenden möchten, funktioniert das Folgende auf beiden Systemen GNU und BSD/Mac sed:

sed -i.bak 's/foo/bar/' filename

Beachten Sie den Platzmangel und den Punkt.

Beweis:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Natürlich könnten Sie dann einfach die .bak - Dateien löschen.

172
kine

Dies funktioniert mit GNU sed, aber nicht unter OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Dies funktioniert unter OS X, jedoch nicht mit GNU sed:

sed -i '' -e 's/foo/bar/' target.file

Unter OS X du

  • kann sed -i -e nicht verwenden, da die Erweiterung der Sicherungsdatei auf -e gesetzt wäre
  • kann sed -i'' -e aus den gleichen Gründen nicht verwenden - es wird ein Leerzeichen zwischen -i und '' benötigt.
102
klickverbot

Unter OSX installiere ich immer GNU sed version via Homebrew, um Probleme mit Skripten zu vermeiden, da die meisten Skripten für GNU sed versions geschrieben wurden.

brew install gnu-sed --with-default-names

Dann wird Ihre BSD sed durch GNU sed ersetzt.

Alternativ können Sie ohne Standardnamen installieren, aber dann:

  • Ändern Sie nach der Installation von gnu-sed Ihren PFAD wie angewiesen
  • Checken Sie Ihre Skripte ein, um je nach System zwischen gsed und sed zu wählen
40
Lewy

Es gibt keine Möglichkeit, es funktionieren zu lassen.

Eine Möglichkeit besteht darin, eine temporäre Datei wie die folgende zu verwenden:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Dies funktioniert bei beiden

17
analogue

Wie Noufal Ibrahim fragt, warum können Sie Perl nicht verwenden? Jeder Mac wird Perl haben, und es gibt sehr wenige Linux- oder BSD-Distributionen, die keine Perl-Version im Basissystem enthalten. Eine der wenigen Umgebungen, in denen Perl tatsächlich fehlen könnte, ist BusyBox (das wie GNU/Linux für -i Funktioniert, außer dass keine Backup-Erweiterung angegeben werden kann).

As ismail empfiehlt,

Da Perl überall verfügbar ist, mache ich einfach Perl -pi -e s,foo,bar,g target.file

und dies scheint in fast jedem Fall eine bessere Lösung zu sein als Skripte, Aliase oder andere Workarounds, um die grundsätzliche Inkompatibilität von sed -i zwischen GNU/Linux und BSD/Mac zu beheben.

16
Alex Dupuy

Antwort: Nein.

Die ursprünglich akzeptierte Antwort macht eigentlich nicht das, was verlangt wird (wie in den Kommentaren vermerkt). (Ich habe diese Antwort gefunden, als ich nach dem Grund gesucht habe, warum ein file-e In meinen Verzeichnissen "zufällig" auftauchte.)

Es gibt anscheinend keine Möglichkeit, sed -i Auf MacOS und Linuces konsistent zum Laufen zu bringen.

Meine Empfehlung ist es, nicht direkt mit sed (mit komplexen Fehlermodi) zu aktualisieren, sondern neue Dateien zu generieren und diese anschließend umzubenennen. Mit anderen Worten: Vermeiden Sie -i.

13
Steve Powell

Das -i Option ist nicht Teil von POSIX Sed . Eine portablere Methode wäre die Verwendung von Vim im Ex-Modus:

ex -sc '%s/alfa/bravo/|x' file
  1. % Alle Zeilen auswählen

  2. s ersetzen

  3. x speichern und schließen

8
Steven Penny

Hier ist eine andere Version, die unter Linux und MacOS funktioniert, ohne eval zu verwenden und ohne Sicherungsdateien löschen zu müssen. Es werden Bash-Arrays zum Speichern der sed -Parameter verwendet. Dies ist sauberer als die Verwendung von eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

Dies erstellt weder eine Sicherungsdatei noch eine Datei mit angehängten Anführungszeichen.

7
nwinkler

Steve Powells Antwort ist ganz richtig. Auf der Seite MAN= für sed unter OSX und Linux (Ubuntu 12.04) wird auf die Unverträglichkeit bei der Verwendung von sed an Ort und Stelle hingewiesen über die beiden Betriebssysteme.

JFYI, bei Verwendung der Linux-Version von sed sollte zwischen -i und Anführungszeichen (die eine leere Dateierweiterung bezeichnen) kein Leerzeichen stehen

sed Linux Man Page

#Linux
sed -i"" 

und

sed OSX Manpage

#OSX (notice the space after the '-i' argument)
sed -i "" 

Ich habe dies in einem Skript mithilfe eines Alias-Befehls und der Ausgabe des Betriebssystemnamens ' uname ' innerhalb einer Bash 'if' umgangen. Der Versuch, betriebssystemabhängige Befehlszeichenfolgen in Variablen zu speichern, war bei der Interpretation der Anführungszeichen ein Hit und Miss. Die Verwendung von ' shopt -s expand_aliases ' ist erforderlich, um die in Ihrem Skript definierten Aliase zu erweitern/zu verwenden. Die Verwendung von shopt wird behandelt hier .

6
Big Rich

Ich bin auf dieses Problem gestoßen. Die einzige schnelle Lösung bestand darin, das Sed in Mac durch die Gnu-Version zu ersetzen:

brew install gnu-sed
1
vikrantt

Wenn Sie sed direkt in einem bash Skript ausführen müssen und NICHT möchten, dass das direkte Ergebnis mit .bkp-Dateien erfolgt und Sie die Möglichkeit haben, das Betriebssystem zu erkennen ( Sagen wir mit ostype.sh ), dann sollte der folgende Hack mit der in bash Shell eingebauten eval funktionieren:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`
1
sdaau

Sie können Schwamm verwenden. Sponge ist ein altes Unix-Programm, das im Paket moreutils enthalten ist (sowohl in Ubuntu als auch wahrscheinlich in Debian und in Homebrew in Mac).

Es puffert den gesamten Inhalt der Pipe, wartet, bis die Pipe geschlossen ist (was wahrscheinlich bedeutet, dass die Eingabedatei bereits geschlossen ist) und überschreibt dann:

Aus der Manpage :

Zusammenfassung

sed '...' Datei | grep '...' | Schwammfeile

0
Guillermo

Folgendes funktioniert für mich unter Linux und OS X:

sed -i' ' <expr> <file>

z.B. für eine Datei f mit aaabbaaba

sed -i' ' 's/b/c/g' f

ergibt aaaccaaca auf Linux und Mac. Beachten Sie, dass eine Zeichenfolge in Anführungszeichen steht mit einem Leerzeichen, wobei kein Leerzeichen zwischen dem -i und die Zeichenfolge. Einfache oder doppelte Anführungszeichen funktionieren beide.

Unter Linux verwende ich bash Version 4.3.11 unter Ubuntu 14.04.4 und auf dem Mac Version 3.2.57 unter OS X 10.11.4 El Capitan (Darwin 15.4.0).

0
David G