Diferenças entre sed no Mac OSX e outro sed "padrão"?

Respostas:

43

O comportamento dos utilitários de shell difere de maneiras menores entre as variantes do unix. Existem muitas variantes unix , com uma história complexa . Existem esforços de padronização , como o padrão POSIX e seu superconjunto da especificação Single UNIX . Atualmente, a maioria dos sistemas implementa o POSIX: 2001, também conhecido como Single UNIX Specification versão 3 , com pequenos desvios e muitas extensões. A especificação Single Unix não é um tutorial, mas a versão 3 é legível se você já tiver uma idéia do que um comando está fazendo. Você pode consultá-lo para saber se algum recurso é padrão ou uma extensão de um sistema específico.

A maioria dos usuários unix usa Linux e não usou nenhuma outra variante. O Linux vem com utilitários GNU , que geralmente têm muitas extensões para o padrão. Então, você encontrará bastante código por aí que funciona no Linux, mas não em outros departamentos, porque depende dessas extensões.

Com relação ao sed, consulte a especificação sed Single Unix para obter o mínimo que todo sistema deve suportar, a página de manual do sistema para o que sua implementação suporta e o manual GNU sed para o que a maioria das pessoas usa.

Uma das extensões não padronizadas no GNU sed é oferecer suporte a vários comandos executados juntos. Por exemplo, este programa GNU sed imprime todas as linhas que contêm um a, mas muda bpara o cprimeiro:

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

{e, }na verdade, são comandos separados; portanto, para portabilidade total, é necessário especificá-los em linhas separadas (em um arquivo) ou em -eargumentos separados (na linha de comando). A falta de um separador de comandos depois {e o uso ;como um separador de comandos são extensões comuns. A falta de um separador de comandos antes }é uma extensão menos comum. Isso é compatível com o padrão:

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

Isso não é padrão, mas geralmente aceito:

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

Outra extensão comum, porém não-padrão, é o uso de \npara significar uma nova linha em um stexto de substituição (o uso em uma regexp é padrão). O método portátil é incluir a nova barra invertida no script sed. Outra extensão comum é \+, \?e \|em regexps, para significar uma ou mais, no máximo uma e alternância; expressões regulares básicas portáteis não possuem nenhuma dessas. Por exemplo, o primeiro comando é uma maneira não portátil de substituir seqüências contíguas de espaço em branco por uma nova linha; o segundo comando é um equivalente compatível com os padrões.

sed -e 's/ \+/\n/'
sed -e 's/  */\
/'
Gilles 'SO- parar de ser mau'
fonte
Observe que em todos os casos sobre extensões GNU, o uso não é padrão. O sedpróprio GNU é compatível, pois faz as coisas permitidas (mas não exigidas, não especificadas) pelo padrão. Há casos em que não é compatível e onde a execução POSIXLY_CORRECTno ambiente pode ajudar. Como com s/[\n]//gisso, é necessário remover a folga e os ncaracteres, mas remover as novas linhas. Ou o comportamento do Ncomando na última linha.
Stéphane Chazelas
sed -ne '/a/ { s/b/c/g; p; }'é padrão desde a edição de 2016 da norma. Sempre foi portátil. Veja austingroupbugs.net/view.php?id=944&nbn=7
Stéphane Chazelas
60

Atualmente, o OS X vem com um sed do FreeBSD a partir de 2005. A maioria das diferenças abaixo também se aplica a outras versões do sed do BSD.

Os usos sed do OS X -Epara usos sed ERE e GNU -r. -Eé um alias para -rno GNU sed (adicionado em 4.2, não documentado até 4.3). Versões mais recentes do FreeBSD e NetBSD sed suportam tanto -Ee -r. O OpenBSD sed suporta apenas -E.

-i ''funciona com o sed do OS X, mas não o GNU sed. -itrabalha com GNU sed, versões recentes do NetBSD, OpenBSD sed, mas não do OS X sed. -i -efunciona com ambos, mas no caso do FreeBSD sedfaz um backup do arquivo original -eanexado ao nome do arquivo (e você não precisa passar mais que uma expressão sed).

GNU interpreta sed sequências de escape como \t, \n, \001, \x01, \w, e \b. O sed do OS X e o POSIX sed interpretam apenas \n(mas não na parte de substituição s).

O GNU sed interpreta \|, \+e \?no BRE, mas o sed do OS X e o POSIX sed não. \(, \), \{, E \}são POSIX BRE.

O GNU sed permite omitir ;ou uma nova linha antes, }mas o sed do OS X não.

i(insert), a(append) e c(change) devem ser seguidos por uma barra invertida e uma nova linha no sed do OS X e no POSIX sed, mas não no GNU sed. Sed GNU acrescenta uma nova linha faltando após o texto inserido por i, aou cmas OS X da sed não. Por exemplo, sed 1iaé uma alternativa ao GNU sed $'1i\\\na\n'.

Por exemplo, printf a|sed -n padiciona uma nova linha no sed do OS X, mas não no GNU sed.

O sed do OS X não suporta modificadores I( que não diferenciam maiúsculas de minúsculas) ou M(multilinhas). Versões mais recentes do suporte ao FreeBSD sed I.

O sed do OS X não suporta -s( --separate), -u( --unbuffered) ou -z( --null-data).

Uma opção BSD que não é suportada pelo GNU sed é a -aque wanexa a um arquivo em vez de truncar um arquivo.

Exemplos de comandos GNU sed que não funcionam com o sed do 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
Lri
fonte
4
-i -enão funciona no OSX. Interpeta -ecomo o sufixo.
26616 Chris Martin
3
@ChrisMartin sim, na versão do OS X -isempre exige um sufixo, mesmo que uma string vazia. então -i '' -edeve funcionar.
waldyrious
@waldyrious Funciona apenas no OSX.
Chris Martin
sim, isso é um capricho do que a versão :)
waldyrious
3
A frase " -i -efunciona com ambos". na sua resposta sugere que existe uma solução de plataforma cruzada. Aparentemente não há.
precisa saber é o seguinte
5

A melhor maneira de descobrir que o mesmo script funciona no Linux e no Mac é:

sed -i.bak -e 's/foo/bar/' -- "${TARGET}" &&
  rm -- "${TARGET}.bak"
vikrantt
fonte
Ou use de perlonde isso -ivem. perl -Tpi -e 's/foo/bar/' -- "$TARGET"
Stéphane Chazelas