sed sul posto flag che funziona sia su Mac (BSD) che Linux


237

Esiste un'invocazione di sedtodo editing sul posto senza backup che funziona sia su Linux che su Mac? Mentre il BSD sedfornito con OS X sembra aver bisogno sed -i '' …, le seddistribuzioni GNU Linux di solito vengono interpretate con le virgolette come nome del file di input vuoto (anziché l'estensione di backup), e necessita sed -i …invece.

Esiste una sintassi della riga di comando che funziona con entrambe le versioni, quindi posso usare lo stesso script su entrambi i sistemi?


Perl non è un'opzione? Devi usare sed?
Noufal Ibrahim,

Forse installa la versione GNU e usala! topbug.net/blog/2013/04/14/… ? Prima ci proverei. Poi di nuovo, anche quel tipo di schifo.
Dmitry Minkovsky,

2
@dimadima: potrebbe essere interessante per alcune altre persone che sfogliano questa domanda che hanno script personali che si rompono sulla loro macchina OS X. Nel mio caso, però, ne avevo bisogno per il sistema di compilazione di un progetto open source, in cui dire al tuo utente di installare GNU sed per primo avrebbe sconfitto lo scopo originale di questo esercizio (patch alcuni file in modo "funziona ovunque") .
dnadlinger,

@klickverbot sì, ha senso. Ho prima aggiunto il commento come risposta, quindi l'ho eliminato, rendendomi conto che non era una risposta alla tua domanda :).
Dmitry Minkovsky,

1
Riferimenti incrociati: come ottenere la portabilità con sed -i (editing sul posto)? su Unix & Linux Stack Exchange.
MvG

Risposte:


212

Se vuoi davvero usare sed -isemplicemente il modo 'facile', il seguente FUNZIONA su GNU e BSD / Mac sed:

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

Nota la mancanza di spazio e il punto.

Prova:

# 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

Ovviamente potresti semplicemente eliminare i .bakfile.


2
Questa è la strada da percorrere. Aggiungi alcuni personaggi ed è portatile. L'eliminazione del file di backup è banale (hai già il nome file quando sed
invoco

Questo ha funzionato su Mac, ma su Ubuntu 16.04 sovrascrive il file originale a 0 byte e crea il file .bak anche con 0 byte?
house9

1
Da segnalare, su MacOS Sierra (e forse anche prima, non ho una macchina a portata di mano), sednon accetta un posizionale commandargomento quando invocato con -i- esso deve essere alimentato con un'altra bandiera, -e. Pertanto, il comando diventa:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
Questo crea backup, mentre l'OP richiede specificamente la modifica sul posto.
Marc-André Lafortune,

8
Quindi la soluzione completa è:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

112

Funziona con GNU sed, ma non su OS X:

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

Funziona su OS X, ma non con GNU sed:

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

Su OS X tu

  • impossibile utilizzare sed -i -epoiché l'estensione del file di backup sarebbe impostata su-e
  • non può essere utilizzato sed -i'' -eper gli stessi motivi: ha bisogno di uno spazio tra -ie ''.

1
@AlexanderMills Dice a sed che l'argomento successivo è un comando - potrebbe anche essere saltato qui.
Benjamin W.

4
Si noti che -ie -i''sono identici al momento dell'analisi della shell; sed non può comportarsi diversamente su quelle due prime invocazioni, perché ottiene gli stessi argomenti esatti.
wchargin,

44

Su OSX, installo sempre la versione GNU sed tramite Homebrew, per evitare problemi negli script, poiché la maggior parte degli script sono stati scritti per le versioni GNU sed.

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

Quindi la tua BSD sed verrà sostituita dalla GNU sed.

In alternativa, puoi installare senza nomi predefiniti, ma poi:

  • Cambia il tuo PATHcome indicato dopo l'installazionegnu-sed
  • Controlla i tuoi script per scegliere tra gsedo in sedbase al tuo sistema

4
è --with-default-namesstato rimosso da homebrew-core , maggiori informazioni in questa risposta. quando si installa gnu-sedora, le istruzioni di installazione specificano che è necessario aggiungere gnubinal PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgerics il

18

Come chiede Noufal Ibrahim , perché non puoi usare Perl? Ogni Mac avrà Perl e ci sono pochissime distribuzioni Linux o BSD che non includono alcune versioni di Perl nel sistema di base. Uno dei pochi ambienti a cui potrebbe effettivamente mancare Perl sarebbe BusyBox (che funziona come GNU / Linux per -i, tranne per il fatto che non è possibile specificare alcuna estensione di backup).

Come consiglia ismail ,

Dal momento che perl è disponibile ovunque lo faccio perl -pi -e s,foo,bar,g target.file

e questa sembra una soluzione migliore in quasi tutti i casi di script, alias o altre soluzioni alternative per gestire l'incompatibilità fondamentale sed -itra GNU / Linux e BSD / Mac.


Adoro questa soluzione. perlfa parte delle specifiche Base standard Linux dal 1 maggio 2009. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/…
OregonTrail

1
Questa è facilmente la migliore soluzione per la portabilità
ecnepsnai,

17

Non c'è modo di farlo funzionare.

Un modo è utilizzare un file temporaneo come:

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

Questo funziona su entrambi


C'è un motivo per cui SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> some / file; invece non funzionerà
Jason Gross il

Sembra che saresti limitato dalla dimensione massima di una variabile bash, no? Non sono sicuro che funzionerà con i file GB.
analogo

3
Se stai creando un file temporaneo, perché non dare semplicemente un'estensione per creare un file di backup (che puoi quindi rimuovere) come suggerisce @kine di seguito?
Alex Dupuy

13

Risposta: No.

La risposta inizialmente accettata in realtà non fa ciò che è richiesto (come indicato nei commenti). (Ho trovato questa risposta mentre cercavo il motivo per cui file-eappariva "casualmente" nelle mie directory.)

Apparentemente non c'è modo di sed -ilavorare costantemente su MacOS e Linuces.

La mia raccomandazione, per quello che vale, non è quella di aggiornare sul posto sed(che ha modalità di errore complesse), ma di generare nuovi file e rinominarli in seguito. In altre parole: evitare -i.


9

L' -iopzione non fa parte di POSIX Sed . Un metodo più portatile sarebbe usare Vim in modalità Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % seleziona tutte le righe

  2. s sostituire

  3. x salva e chiudi


Questa è la risposta corretta Una caratteristica chiave di POSIX è la portabilità.
Kajukenbo,

9

Ecco un'altra versione che funziona su Linux e macOS senza utilizzare evale senza dover eliminare i file di backup. Utilizza array Bash per la memorizzazione dei sedparametri, che è più pulito rispetto all'utilizzo di 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

Questo non crea un file di backup, né un file con virgolette aggiunte.


2
questa è la risposta corretta Lo semplificherei un po ', ma l'idea funziona perfettamente sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Alex Skrypnyk,

Roba buona! Preferisco la versione più lunga per leggibilità, però.
nwinkler

1
Questo è il modo migliore per me di essere compatibile con sistemi diversi.
Victor Choy,

6

La risposta di Steve Powell è abbastanza corretta, consultando la pagina MAN per sed su OSX e Linux (Ubuntu 12.04) si evidenzia la non compatibilità all'interno dell'utilizzo sed "sul posto" tra i due sistemi operativi.

JFYI, non ci dovrebbe essere spazio tra -i e le virgolette (che indicano un'estensione di file vuota) usando la versione Linux di sed, quindi

sed Linux Man Page

#Linux
sed -i"" 

e

Pagina man di OSX sed

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

Ho risolto questo problema in uno script usando un comando alias'd e l'output del nome OS di ' uname ' all'interno di un bash 'if'. Il tentativo di memorizzare stringhe di comandi dipendenti dal sistema operativo in variabili è stato incostante durante l'interpretazione delle virgolette. L'uso di ' shopt -s expand_aliases ' è necessario per espandere / usare gli alias definiti all'interno del tuo script. L'uso di shopt è trattato qui .


1

Se è necessario eseguire sedsul posto in uno bashscript e NON si desidera che il risultato sul posto risulti con i file .bkp e si dispone di un modo per rilevare il sistema operativo (ad esempio, utilizzando ostype.sh ), - allora il il seguente hack con la bashshell integrata evaldovrebbe funzionare:

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""`

0

Puoi usare la spugna. Sponge è un vecchio programma unix, che si trova nel pacchetto moreutils (sia in Ubuntu che probabilmente in Debian, e in homebrew in Mac).

Buffererà tutto il contenuto della pipe, attenderà che la pipe sia chiusa (probabilmente significa che il file di input è già vicino) e quindi sovrascriverà:

Dalla pagina man :

Sinossi

file sed '...' | grep '...' | file spugna


3
Approccio gradevole: sfortunatamente non aiuta davvero nella situazione originale poiché la ragione per ricorrere a sed era di avere qualcosa da usare in uno script helper multipiattaforma usato su macchine client, dove non si può dipendere da nient'altro che dal sistema strumenti installati. Potrebbe essere utile a qualcun altro, però.
Dnadlinger,

0

Quanto segue funziona per me su Linux e OS X:

sed -i' ' <expr> <file>

ad es. per un file fcontenenteaaabbaaba

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

rese aaaccaacasia su Linux che su Mac. Nota che c'è una stringa tra virgolette che contiene uno spazio , senza spazio tra-i e la stringa. Le virgolette singole o doppie funzionano entrambe.

Su Linux sto usando la bashversione 4.3.11 sotto Ubuntu 14.04.4 e su Mac la versione 3.2.57 sotto OS X 10.11.4 El Capitan (Darwin 15.4.0).


1
Wow, sono contento di non aver ascoltato tutti quelli che hanno detto che era impossibile e ho continuato a leggere! Funziona davvero. Grazie!
Personman,

4
Non funziona per me su Mac OS ... viene creato un nuovo file con spazio aggiunto alla fine del nome file
Frédéric,

Non funziona nemmeno per me su Mac (/ usr / bin / sed) - Ora ho un file di backup aggiuntivo con uno spazio aggiunto al nome del file :-(
paul_h

Prova a togliere lo spazio dalle virgolette singole, funziona per me.
dps,

0

Ho riscontrato questo problema. L'unica soluzione rapida era sostituire sed in mac con la versione gnu:

brew install gnu-sed

Questo duplica una risposta esistente con molti più dettagli dal 2015.
Tripleee
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.