Novas linhas no sed no Mac OS X

44

Acho que \nisso não funciona no sed no Mac OS X. Especificamente, digamos que quero dividir as palavras separadas por um único espaço em linhas:

# input
foo bar

Eu uso,

echo "foo bar" | sed 's/ /\n/'

Mas o resultado é estúpido, o \nnão é escapado!

foonbar

Depois de consultar o google, encontrei uma solução alternativa :

echo 'foo bar' | sed -e 's/ /\'$'\n/g'

Depois de ler o artigo, ainda não consigo entender o que \'$'\n/g'significa. Alguém pode me explicar ou se existe alguma outra maneira de fazer isso? Obrigado!

Ivan ZG Xiao
fonte
isso provavelmente funcionaria também:echo "foo bar" | tr ' ' '\n'
Glenn Jackman
3
Obrigado pelo conselho. Mas atualmente apenas uso o caso acima como exemplo, preciso saber como escapar de a \n.
Ivan ZG Xiao
Possível duplicado: stackoverflow.com/questions/21621722/...
hyiltiz

Respostas:

27

Você pode brew install gnu-sedsubstituir as chamadas sedpor gsed.

Se não quiser acrescentar o "g" a sed, você pode brew install gnu-sed --with-default-namese basta ligar sed.

(Editar: bandeira de infusão atualizada, dica de chapéu para Clément.)

Ahmed Fasih
fonte
Usar o mesmo software em todas as plataformas é muito mais fácil (finalmente para mim) do que lidar com todas as especificidades da versão para Mac. Cuidado, a opção é agora --with-default-namese não --default-names . No entanto, essa opção não funcionou na minha instalação, então tive que colocar um alias gsed=sedno meu ~/.profilepara fazê-lo funcionar.
Clément
4
Esta é uma solução feia. Ele não explica por que o sedOS X se comporta da maneira que faz e assume a suposição incorreta gnu-sedmais correta. Não seja viciado em GNU e siga os padrões POSIX para evitar problemas a longo prazo.
octosquidopus
É assim que a Apple deve fazer seu sistema operacional funcionar por padrão. Por que usar o nome de um programa e depois fazê-lo funcionar de maneira diferente? I perder uma hora por causa disso ...
Rascio
@rascio - porque o OS X é semelhante ao BSD e o Linux é semelhante ao Linux.
iAdjunct
@rascio Acho que você verá que o GNU sed é o novato; O BSD sed data de 1979 (veja en.wikipedia.org/wiki/Version_7_Unix ).
Duncan Bayne
24

Isso também funcionaria:

echo 'foo bar' | sed 's/ /\
/g'

echo 'foo bar' | sed $'s/ /\\\n/g'

lf=$'\n'; echo 'foo bar' | sed "s/ /\\$lf/g"

O sed do OS X não interpreta \nno padrão de substituição, mas você pode usar um avanço de linha literal precedido por um caractere de continuação de linha. O shell substitui $'\n'por um avanço de linha literal antes do comando sed ser executado.

Lri
fonte
4
Por que sed $'s/ /\\\n/g'funciona, mas não sed $'s/\\\n/ /g'?
Alec Jacobson
11
@alec Você não pode usar sednem no linux / unix para remover novas linhas porque elas são analisadas / divididas em cada nova linha. Se você executar este em linux / unix, ele não vai fazer nada também:echo -e 'foo\nbar' | sed 's/\n//'
wisbucky
10

A solução alternativa que você encontrou passa uma única sequência de argumentos para sed -e.

Esse argumento acaba sendo uma string no s/ / /gformato sed familiar .

Essa sequência é criada em duas partes, uma após a outra.

A primeira parte é citada no '...'formulário.

A segunda parte é citada no $'...'formulário.

A 's/ /\'parte retira as aspas simples, mas passa para sed assim como aparece na linha de comando. Ou seja, a barra invertida não é comida pelo bash, é passada para sed.

A $'\n/g'peça obtém o sinal de cifrão e as aspas simples, e \né convertida em um caractere de nova linha.

Todos juntos, o argumento se torna

s / / \ newline/ g

[Foi divertido. Demorou um pouco para desembrulhar isso. +1 para uma pergunta interessante.]

Spiff
fonte
7

A expressão $'...'é um bashismo que produz ...com as seqüências de escape padrão expandidas. º\' antes que significa apenas uma barra invertida seguida pela final da seção citada, a cadeia resultante é s/ /\. (Sim, você pode alternar entre aspas no meio de uma sequência; ela não termina a sequência.)

O padrão POSIX sedaceita apenas \ncomo parte de um padrão de pesquisa. O OS X usa o FreeBSD sed, que é estritamente compatível com POSIX; O GNU, como sempre, adiciona coisas extras e, em seguida, os usuários do Linux pensam que isso é algum tipo de "padrão" (talvez eu ficasse mais impressionado se algum deles tivesse um processo de padrões).

geekosaur
fonte
Agora eu entendo a $'...'parte. Mas ... o que é s/ /\ ? Como assim switch quoting?
Ivan ZG Xiao
2
'...'é um tipo de citação de shell; $'...'é outro. Há também "..."e \xpara citar um único personagem. Você pode combinar aqueles em uma única palavra, que é o que estava sendo feito lá, alternando de uma ''string normal para uma $''string para traduzir o \n. Quanto ao resto, está construindo um sedcomando ( s/text/replacement/flags). Nesse caso, o comando é iniciado, incluindo uma barra invertida no final para proteger a nova linha literal $'\n/g'anexada. O resultado é substituir todos os /gespaços (o sinalizador) por novas linhas.
Geekosaur
1

É muito fácil ver visualmente o que está acontecendo. Simplesmente ecoar a corda!

echo 's/$/\'$'\n/g'

resulta em

s/$/\
/g

que é equivalente a s/$/\newline/g

Se você não tivesse o extra \antes do newline, o shell interpretaria newlineo final do comando prematuramente.

wisbucky
fonte