O sed pode substituir novos caracteres de linha?


43

Existe algum problema com o caracter sed e nova linha?
Eu tenho um arquivo test.txt com o seguinte conteúdo

aaaaa  
bbbbb  
ccccc  
ddddd  

O seguinte não funciona:
sed -r -i 's/\n/,/g' test.txt

Eu sei que posso usar trisso, mas minha pergunta é por que não parece possível com o sed.

Se esse é um efeito colateral do processamento do arquivo linha por linha, eu estaria interessado em saber por que isso acontece. Eu acho que grepremove novas linhas. Sed faz o mesmo?


1
Nesse caso, o sed pode não ser a melhor ferramenta a ser usada (por exemplo, "tr"). Existem ferramentas mais intuitivas, mais fáceis de ler / manter, com melhor desempenho (principalmente em big data) etc. ... Não use o martelo para colocar os parafusos (mesmo que funcione). Você pode encontrar uma comparação em: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser

2
tradicionaria um final ,e produziria uma linha não terminada. Melhor é usar paste:paste -sd , test.txt
Stéphane Chazelas 10/17/17

Respostas:


49

Com GNU sede fornecido POSIXLY_CORRECTnão está no ambiente (para entrada de linha única):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

Em https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. crie um rótulo via :a
  2. acrescente a linha atual e a próxima linha ao espaço do padrão via N
  3. se estivermos antes da última linha, ramifique para o rótulo criado $!ba( $!significa não fazê-lo na última linha (pois deve haver uma nova linha final)).
  4. finalmente, a substituição substitui cada nova linha por uma vírgula no espaço do padrão (que é o arquivo inteiro).

Isso parece indicar que o problema é que o sed lê linha por linha. Mas eu não consigo entender por que isso é um problema. Ele poderia apenas ler a linha e substituir o novo caractere de linha (ou último caractere) por um,
Jim

1
@ Jim Parece que não está no buffer a ser correspondido, mas eu não sou fluente com sed, talvez alguém possa esclarecer isso. Eu acho que você deve estender seu Q com essas informações específicas, para que as pessoas tenham maior probabilidade de lê-las e espero responder.
Anthon

Isso resulta emba: Event not found
krb686

@ krb686 Qual é o "Isso" a que você está se referindo? Você executou o sedcomando acima com essas opções exatas? Em qual test.txt arquivo? Com qual versão do sed(tentar sed --version)?
Anthon

@ Anthon Desculpe, acho que pretendia dizer "o". Eu li outro post do SO que me informou que o csh exige que eu escape do !. Curiosamente, isso ainda não funcionou para mim e acabei tendo que escapar duas vezes do !meu .cshscript. Então, eu realmente não tenho um problema no momento, mas você sabe por que isso pode ser? O que funcionou para mim foised :a;N;$\\!ba;s/\n/ /g'
krb686

17

Isso funciona com o GNU sed:

sed -z 's/\n/,/g' 

-z está incluído desde 4.2.2

NB -zaltera o delimitador para caracteres nulos ( \0). Se sua entrada não contiver caracteres nulos, toda a entrada será tratada como uma única linha. Isso pode vir com suas limitações .

Para evitar a substituição da nova linha da última linha, você pode alterá-la novamente:

sed -z 's/\n/,/g;s/,$/\n/'

(Que é a sedsintaxe do GNU novamente, mas não importa, porque tudo é apenas GNU)


3
Isso também substituirá a nova linha à direita, que pode não ser o que o OP deseja ... compare o resultado com a solução da mikeserv .
don_crissti

7

No site da Oracle:

O utilitário sed funciona lendo sequencialmente um arquivo, linha por linha, na memória. Em seguida, ele executa todas as ações especificadas para a linha e coloca a linha de volta na memória para despejar no terminal com as alterações solicitadas. Depois que todas as ações ocorrerem nessa linha, ela lê a próxima linha do arquivo e repete o processo até concluir o arquivo.

Basicamente, isso significa que, porque sed está lendo linha por linha, o caractere de nova linha não é correspondido.

A solução em https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n é:

sed ':a;N;$!ba;s/\n/,/g'

ou, em uma versão portátil (sem ;concatenar após os rótulos das marcas de salto)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

Uma explicação sobre como isso funciona é fornecida nessa página.


Usei uma forma modificada para analisar os logs da VPN e colocar o usuário "autenticado" e as informações de registro de data e hora na mesma linha. Felicidades!
user208145

Observe que essa sintaxe é específica do GNU e, mesmo com o GNU sed, se POSIXLY_CORRECT estiver no ambiente e a entrada tiver apenas uma linha, não haverá saída.
Stéphane Chazelas

5

sedsempre remove o \newline à direita antes de preencher o espaço do padrão e, em seguida, acrescenta um antes de escrever os resultados do script. Uma linha de \new pode ser obtida no espaço do padrão por vários meios - mas nunca se não for o resultado de uma edição. Isso é importante - as \nlinhas no sedespaço do padrão sempre refletem uma alteração e nunca ocorrem no fluxo de entrada. \nAs linhas de linha são o único delimitador sedem que o contador pode contar com informações desconhecidas.

Se você deseja substituir todas as \nlinhas eletrônicas por vírgulas e seu arquivo não for muito grande, faça o seguinte:

sed 'H;1h;$!d;x;y/\n/,/'

Isso anexa todas as linhas de entrada ao hespaço antigo - exceto o primeiro, que substitui o hespaço antigo - após um \ncaractere de linha de linha. Ele dexclui todas as linhas, não as $!últimas da saída. Na última linha H, os espaços antigos e padrão são xalterados e todos os \ncaracteres da linha são y///traduzidos para vírgulas.

Para arquivos grandes, esse tipo de coisa provavelmente causará problemas - sedo buffer dos limites de linha, que pode ser facilmente transbordado com ações desse tipo.


2

Como alternativa, você pode usar uma sintaxe um pouco mais simples:

sed ':a;N;s/\n/,/g;ba'

... apenas mudando a ordem da sequência.


3
Mas executa o scomando para cada linha de entrada em um espaço padrão cada vez maior.
Stéphane Chazelas

1

Há uma mágica sed muito agradável aqui. E alguns bons pontos levantados sobre o excesso de espaço do padrão. Adoro usar o sed, mesmo quando não é o caminho mais simples, porque é muito compacto e poderoso. No entanto, ele tem limitações e, para grandes quantidades de dados, o espaço do padrão teria que ser mahoosivo.

O GNU diz o seguinte:

Para aqueles que desejam escrever scripts sed portáteis, saiba que algumas implementações limitam os comprimentos de linha (para o padrão e os espaços de espera) a não mais que 4000 bytes. A norma posix especifica que implementações sed conformes devem suportar pelo menos 8192 bytes de comprimento de linha. O GNU sed não tem limite embutido no comprimento da linha; contanto que ele possa malloc () mais memória (virtual), você pode alimentar ou construir linhas pelo tempo que quiser.
No entanto, a recursão é usada para lidar com subpadrões e repetição indefinida. Isso significa que o espaço de pilha disponível pode limitar o tamanho do buffer que pode ser processado por certos padrões.

Não tenho muito a acrescentar, mas gostaria de apontá-lo para o meu guia para sed . É excelente. http://www.grymoire.com/Unix/Sed.html

e aqui está a minha solução:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

bem, funciona



-1

Digamos que você queira substituir as novas linhas por \n. Eu queria fazer isso, então aqui está o que eu fiz:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Aqui está o que ele faz: para todas as linhas, exceto a última , acrescente \n. Em seguida, exclua as novas linhas com tr.


-restá disponível apenas no GNU sed, não no BSD.
kenorb 11/09
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.