Differenze tra sed su Mac OSX e altre sed "standard"?


Risposte:


43

Il comportamento delle utility della shell differisce in modi minori tra le varianti unix. Esistono molte varianti unix , con una storia complessa . Ci sono sforzi di standardizzazione come lo standard POSIX e il suo superset la specifica Single UNIX . Oggi la maggior parte dei sistemi implementa POSIX: 2001, noto anche come Versione 3 della specifica UNIX singola , con deviazioni minori e molte estensioni. La specifica Single Unix non è un tutorial, ma la versione 3 è leggibile se hai già un'idea di cosa sta facendo un comando. Puoi consultarlo per sapere se alcune funzionalità sono standard o un'estensione di un particolare sistema.

La maggior parte degli utenti unix usa Linux e non ha usato nessuna altra variante. Linux viene fornito con utility GNU , che spesso hanno molte estensioni allo standard. Quindi troverai un sacco di codice là fuori che funziona su Linux ma non su altri unices, perché si basa su quelle estensioni.

Per quanto riguarda sed, consulta le specifiche sed Single Unix per il minimo che ogni sistema dovrebbe supportare, la pagina man sul tuo sistema per ciò che supporta la tua implementazione e il manuale GNU sed per ciò che la maggior parte delle persone là fuori usano.

Una delle estensioni non standard in GNU sed sta supportando più comandi eseguiti insieme. Ad esempio, questo programma sed GNU stampa tutte le righe che contengono un a, ma cambia bper cprimo:

sed -ne '/a/ {s/b/c/g; p}'

{e in }realtà sono comandi separati, quindi per la completa portabilità, è necessario specificarli su righe separate (in un file) o in -eargomenti separati (sulla riga comandi). La mancanza di un separatore di comandi dopo {e l'uso di ;come separatore di comandi sono estensioni comuni. La mancanza di un separatore di comandi prima }è un'estensione meno comune. Questo è conforme allo standard:

sed -n -e '/a/ {' -e 's/b/c/g' -e p -e '}'

Questo è non standard ma comunemente accettato:

sed -ne '/a/ { s/b/c/g; p; }'

Un'altra estensione non standard ma comune è l'uso di \nsignificare una nuova riga in un stesto sostitutivo (l'uso in una regexp è standard). Il metodo portatile è quello di includere backslash-newline nello script sed. Un'altra estensione comune è \+, \?e \|in regexps, per indicare una o più, al massimo una e l'alternanza; le espressioni regolari di base portatili non hanno nessuna di queste. Ad esempio, il primo comando è un modo non portatile di sostituire sequenze contigue di spazi bianchi con una nuova riga; il secondo comando è un equivalente conforme agli standard.

sed -e 's/ \+/\n/'
sed -e 's/  */\
/'

Si noti che in tutti quei casi relativi alle estensioni GNU, l'utilizzo non è standard. GNU sedstesso è conforme in quanto fa le cose consentite (ma non richieste, non specificate) dallo standard. Ci sono casi in cui non è conforme e dove POSIXLY_CORRECTpuò essere di aiuto eseguirlo nell'ambiente. Come in s/[\n]//gquesto caso è necessario rimuovere gioco e ncaratteri, ma rimuovere invece le nuove righe. O il comportamento del Ncomando sull'ultima riga.
Stéphane Chazelas,

sed -ne '/a/ { s/b/c/g; p; }'è standard dall'edizione 2016 dello standard. Era sempre portatile. Vedi austingroupbugs.net/view.php?id=944&nbn=7
Stéphane Chazelas il

60

OS X attualmente viene fornito con una versione di FreeBSD sed del 2005. La maggior parte delle differenze di seguito si applica anche ad altre versioni di BSD sed.

Gli usi sed di OS X per gli usi sed -Edi ERE e GNU sed -r. -Eè un alias per -rin GNU sed (aggiunto in 4.2, non documentato fino a 4.3). Le versioni più recenti di FreeBSD e NetBSD sed supportano sia -Ee -r. OpenBSD sed supporta solo -E.

-i ''funziona con sed di OS X ma non con GNU sed. -ifunziona con GNU sed, versioni recenti di NetBSD, OpenBSD sed, ma non con OS X sed. -i -efunziona con entrambi, ma nel caso di FreeBSD sedesegue un backup del file originale con l' -eaggiunta al nome del file (e non devi passare più di un'espressione a sed).

GNU sed interpreta sequenze di escape come \t, \n, \001, \x01, \w, e \b. Le versioni sed e POSIX di OS X interpretano solo \n(ma non nella parte sostitutiva di s).

GNU sed interpreta \|, \+e \?in BRE ma sed X di OS X e POSIX no. \(, \), \{, E \}sono POSIX BRE.

GNU sed consente di omettere ;o una nuova riga prima }ma OS X sed non lo fa.

i(insert), a(append) e c(change) devono essere seguiti da una barra rovesciata e da una nuova riga nella versione OS X e POSIX ma non nella versione GNU sed. Sed GNU aggiunge un ritorno a capo mancante dopo il testo inserito da i, ao cma OS X di sed non lo fa. Ad esempio sed 1iaè un'alternativa GNU a sed $'1i\\\na\n'.

Ad esempio printf a|sed -n paggiunge una nuova riga nella versione di OS X ma non nella versione di GNU.

La sed di OS X non supporta i modificatori I(senza distinzione tra maiuscole e minuscole) o M(multilinea). Versioni più recenti del supporto sed di FreeBSD I.

La sed di OS X non supporta -s( --separate), -u( --unbuffered) o -z( --null-data).

Un'opzione BSD che non è supportata da GNU sed è -a, che rende wappend a un file invece di troncare un file.

Esempi di comandi GNU sed che non funzionano con sed di OS X:

sed /pattern/,+2d # like `sed '/pattern/{N;N;d;}'`
sed -n 0~3p # like `awk NR%3==0`
sed /pattern/Q # like `awk '/pattern/{exit}1'` or `sed -n '/pattern/,$!p'`
sed 's/\b./\u&/g' # \u converts the next character to uppercase
sed 's/^./\l&/' # \l converts the next character to lowercase
sed -i '1ecat file_to_prepend' file # e executes a shell command
sed -n l0 # 0 disables wrapping

4
-i -enon funziona su OSX. Si interseca -ecome suffisso.
Chris Martin,

3
@ChrisMartin sì, nella versione OS X -irichiede sempre un suffisso, anche se una stringa vuota. quindi -i '' -edovrebbe funzionare.
waldyrious

@waldyrious Funziona solo su OSX.
Chris Martin,

sì, è una stranezza di quella versione :)
waldyrious il

3
La frase " -i -efunziona con entrambi". nella tua risposta suggerisce che esiste una soluzione multipiattaforma. Apparentemente non c'è.
leondepeon,

5

Il modo migliore che ho trovato per avere lo stesso lavoro di script sia su Linux che su Mac è di:

sed -i.bak -e 's/foo/bar/' -- "${TARGET}" &&
  rm -- "${TARGET}.bak"

O usa da perldove -iproviene. perl -Tpi -e 's/foo/bar/' -- "$TARGET"
Stéphane Chazelas,
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.