Como posso substituir uma nova linha (\ n) usando sed?


1371

Como posso substituir uma nova linha (" \n") por um espaço (" ") usando o sedcomando?

Tentei sem sucesso:

sed 's#\n# #g' file
sed 's#^$# #g' file

Como faço para corrigir isso?


27
tré apenas a ferramenta certa para o trabalho se substituir um único caractere por um único caractere, enquanto o exemplo acima mostra substituir nova linha por um espaço. Portanto, no exemplo acima, tr poderia funcionar .. Mas seria limitador posteriormente.
irritada 84

9
trna ferramenta certa para o trabalho, porque o interlocutor queria substituir cada nova linha por um espaço, conforme mostrado em seu exemplo. A substituição de novas linhas é exclusivamente misteriosa, sedmas é facilmente realizada tr. Esta é uma pergunta comum. Realizar substituições de regex não é feito trapenas por sed, mas que seria a ferramenta certa ... para uma pergunta diferente.
Mike S

3
"tr" também pode simplesmente excluir a nova linha `tr -d '\ n'`, mas você também pode excluir os retornos para ser mais universal `tr -d '\ 012 \ 015' '.
anthony

2
AVISO: "tr" age de maneira diferente em relação a um intervalo de caracteres entre o Linux e as máquinas Solaris mais antigas (EG sol5.8). EG: `tr-d 'az' 'e` tr-d' [az] ''. Por isso, recomendo que você use "sed", que não tem essa diferença.
anthony

2
@ MikeS Obrigado pela resposta. Siga tr '\012' ' 'com um echo. Caso contrário, o último avanço de linha no arquivo também será excluído. tr '\012' ' ' < filename; echofaz o truque.
Bernie Reiter

Respostas:


1514

Use esta solução com o GNU sed:

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

Isso lerá o arquivo inteiro em um loop e substituirá a (s) nova (s) linha (s) por um espaço.

Explicação:

  1. Crie um rótulo via :a.
  2. Anexe 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 toda nova linha por um espaço no espaço padrão (que é o arquivo inteiro).

Aqui está uma sintaxe compatível com várias plataformas que funciona com BSD e OS X sed(conforme comentário do @Benjie ):

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

Como você pode ver, usar sedpara esse problema simples é problemático. Para uma solução mais simples e adequada, consulte esta resposta .


45
@Arjan e Masi: O OS X usa BSDs em sedvez de GNU sed, então pode haver algumas diferenças sutis (e outras nem tanto) nos dois. Isso é uma dor constante se você trabalha em máquinas OS X e * nix. Normalmente instalo GNU coreutilse findutilsno OS X e ignoro as versões do BSD.
Telêmaco

50
Não :aé um registro, é um rótulo de filial. É um alvo para o bcomando *, que funciona como "goto". Chamá-lo de registro implica que você pode criar locais de armazenamento. Existem apenas dois "registros"; um é chamado de "espaço de espera" que seu script não está usando e o outro é chamado de "espaço de padrão". O Ncomando anexa uma nova linha e a próxima linha do arquivo de entrada ao espaço do padrão. [* Você pode ter vários rótulos e bcomandos. Se você possui um bcomando sem um rótulo, ele se ramifica no final do script para ler a próxima linha e faz um loop novamente.]
Pausado até novo aviso.

108
Você pode executar essa plataforma cruzada (ou seja, no Mac OS X) executando os comandos separadamente, em vez de separar com ponto e vírgula: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'
Benjie

74
Por que alguém não está comentando sobre o que é uma bagunça idiota (não a resposta em si, mas o programa para o qual a resposta proposta é a melhor solução para um problema muito simples). Sed parece um carro que geralmente funciona bem, mas se você quiser dirigir para uma rua específica nas proximidades, a única maneira é levantar o carro com um helicóptero.
Arca-kun

12
Vamos lá pessoal - 261 votos positivos para uma solução louca e incompreensível que não funciona ???? O sed é uma excelente ferramenta para substituições simples em uma única linha; para qualquer outra coisa, basta usar o awk. Good grief ....
Ed Morton

1711

seddestina-se a ser usado na entrada baseada em linha. Embora possa fazer o que você precisa.


Uma opção melhor aqui é usar o trcomando da seguinte maneira:

tr '\n' ' ' < input_filename

ou remova totalmente os caracteres da nova linha:

tr -d '\n' < input.txt > output.txt

ou se você possui a versão GNU (com suas opções longas)

tr --delete '\n' < input.txt > output.txt

88
Sed é baseado em linhas, portanto, é difícil entender novas linhas.
Alexander Gladysh

191
O sed funciona em um "fluxo" de entrada, mas o compreende em pedaços delimitados por nova linha. É uma ferramenta unix, o que significa que faz uma coisa muito bem. A única coisa é "trabalhar em um arquivo em linha". Fazê-lo fazer outra coisa será difícil e corre o risco de ser de buggy. A moral da história é: escolha a ferramenta certa. Muitas de suas perguntas parecem assumir a forma "Como posso fazer com que esta ferramenta faça algo que nunca foi feito?" Essas perguntas são interessantes, mas se surgirem no decorrer da solução de um problema real, você provavelmente está fazendo errado.
dmckee --- gatinho ex-moderador

7
A @JBBrown tré uma jóia frequentemente esquecida na construção de oleodutos.
dmckee --- gatinho ex-moderador

70
tr é ótimo, mas você só pode substituir novas linhas por caracteres únicos. Você precisa usar uma ferramenta diferente se você deseja substituir newlines com uma corda
Eddy

21
@Eddy - Eu costumava tr para substituir novas linhas com um personagem que não aparecem no texto (eu usei crase), em seguida, sed para substituir o backtick com a seqüência que eu queria usar
rjohnston

494

Resposta rápida

sed ':a;N;$!ba;s/\n/ /g' file
  1. : a crie um rótulo 'a'
  2. N anexa a próxima linha ao espaço do padrão
  3. $! se não for a última linha , ba branch (vá para) rotule 'a'
  4. s substitui , / \ n / regex por nova linha , / / por um espaço , / g de correspondência global (quantas vezes for possível)

O sed percorrerá as etapas de 1 a 3 até alcançar a última linha, ajustando todas as linhas no espaço padrão onde o sed substituirá todos os caracteres \ n


Alternativas

Todas as alternativas, ao contrário do sed , não precisarão chegar à última linha para iniciar o processo

com bash , lento

while read line; do printf "%s" "$line "; done < file

com velocidade perl , tipo sed

perl -p -e 's/\n/ /' file

com tr , mais rápido que sed , pode substituir por apenas um caractere

tr '\n' ' ' < file

com pasta , velocidade tipo tr , pode substituir por apenas um caracter

paste -s -d ' ' file

com velocidade awk , tipo tr

awk 1 ORS=' ' file

Outra alternativa como "echo $ (<file)" é lenta, funciona apenas em arquivos pequenos e precisa processar o arquivo inteiro para iniciar o processo.


Resposta longa da FAQ sed 5.10

5.10 Por que não consigo combinar ou excluir uma nova linha usando a
seqüência de escape \ n ? Por que não consigo combinar duas ou mais linhas usando \ n?

O \ n nunca corresponderá à nova linha no final da linha, porque a
nova linha é sempre removida antes da linha ser colocada no
espaço do padrão. Para inserir 2 ou mais linhas no espaço do padrão, use
o comando 'N' ou algo semelhante (como 'H; ...; g;').

O Sed funciona assim: o sed lê uma linha de cada vez, corta a
nova linha final, coloca o que resta no espaço do padrão onde
o script sed pode endereçá-lo ou alterá-lo e, quando o espaço do padrão
é impresso, anexa uma nova linha ao stdout (ou para um arquivo). Se o
espaço do padrão for total ou parcialmente excluído com 'd' ou 'D', a
nova linha não será adicionada nesses casos. Assim, scripts como

  sed 's/\n//' file       # to delete newlines from each line             
  sed 's/\n/foo\n/' file  # to add a word to the end of each line         

NUNCA funcionará, porque a nova linha à direita é removida antes que
a linha seja colocada no espaço do padrão. Para executar as tarefas acima,
use um destes scripts:

  tr -d '\n' < file              # use tr to delete newlines              
  sed ':a;N;$!ba;s/\n//g' file   # GNU sed to delete newlines             
  sed 's/$/ foo/' file           # add "foo" to end of each line          

Como versões do sed diferente do GNU sed têm limites para o tamanho do
buffer de padrão, o utilitário 'tr' do Unix é o preferido aqui.
Se a última linha do arquivo contiver uma nova linha, o GNU sed adicionará
essa nova linha à saída, mas excluirá todas as outras, enquanto tr
excluirá todas as novas linhas.

Para corresponder a um bloco de duas ou mais linhas, há três opções básicas:
(1) use o comando 'N' para adicionar a linha Próxima ao espaço do padrão;
(2) use o comando 'H' pelo menos duas vezes para anexar a linha atual
ao espaço de espera e, em seguida, recupere as linhas do espaço de espera
com x, g ou G; ou (3) use intervalos de endereços (consulte a seção 3.3 acima)
para corresponder às linhas entre dois endereços especificados.

As opções (1) e (2) colocam \ n no espaço do padrão, onde
podem ser endereçadas conforme desejado ('s / ABC \ nXYZ / alfabeto / g'). Um exemplo
de uso de 'N' para excluir um bloco de linhas aparece na seção 4.13
("Como faço para excluir um bloco de linhas consecutivas específicas ?"). Este
exemplo pode ser modificado alterando o comando delete para
outra coisa , como 'p' (impressão), 'i' (inserção), 'c' (alteração), 'a' (anexo)
ou 's' (substituto) .

Choice (3) não vai colocar um \ n no espaço padrão, mas não
coincidir com um bloco de linhas consecutivas, por isso pode ser que você não
precisa mesmo o \ n para encontrar o que você está procurando. Desde o GNU sed
versão 3.02.80 agora suporta esta sintaxe:

  sed '/start/,+4d'  # to delete "start" plus the next 4 lines,           

além dos
endereços de intervalo tradicionais '/ daqui /, / para lá / {...}' , pode ser possível evitar o uso de \ n completamente.


6
trfoi uma ótima idéia e sua cobertura geral contribui para uma resposta de alta qualidade.
New Alexandria

1
+1 para usar ( utilitário padrão ) paste... e todos os outros!
Totor 15/03


4
A melhor parte dessa resposta é que a "resposta longa" explica exatamente como e por que o comando funciona.
Pdwalker 2/05

3
Essa pode ser a mais útil das milhares de respostas que li no stackexchange. Preciso combinar vários caracteres entre linhas. Nenhum exemplo sed anterior abordou várias linhas e tr não pode lidar com a correspondência de vários caracteres. Perl parece bom, mas não está funcionando como eu esperava. Eu votaria nessa resposta várias vezes, se pudesse.
Mightypile

225

Uma alternativa awk mais curta:

awk 1 ORS=' '

Explicação

Um programa awk é constituído por regras que consistem em blocos de código condicionais, ou seja:

condition { code-block }

Se o code-block for omitido, o padrão é usado: { print $0 }. Assim, o 1é interpretado como uma condição verdadeira e print $0é executado para cada linha.

Quando awklê a entrada, ela a divide em registros com base no valor de RS(Separador de Registros), que por padrão é uma nova linha, portanto awk, por padrão, analisará a entrada em linhas. A divisão também envolve a remoção RSdo registro de entrada.

Agora, ao imprimir um registro, ORS(Separador de registros de saída) é anexado a ele, o padrão é novamente uma nova linha. Portanto, alterando ORSpara um espaço, todas as novas linhas são alteradas para espaços.


5
Eu gosto muito dessa solução simples, que é muito mais legível, do que outros
Fedir RYKHTIK

8
Se fizer mais sentido, isso poderia ser efetivamente escrito como: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt(adicionando uma nova linha final apenas para ilustrar o início / fim); o "1" é avaliado para true(processar a linha) e print(imprimir a linha). Um condicional também pode ser adicionado a esta expressão, por exemplo, apenas trabalhando em linhas que correspondam a um padrão: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} '
michael

2
Você pode fazer isso de forma mais simples: codeawk 'ORS = ""' file.txtcode
Udi 28/03

Ao usar o awk assim, infelizmente, o último feed de linha do arquivo também é excluído. Veja a resposta de Patrick Dark acima sobre o uso de 'tr' em um subshell como `cat file | echo $ (tr "\ 012" "") `, que faz o truque. Bacana.
Bernie Reiter

143

O gnu sed tem uma opção -zpara registros separados por nulo (linhas). Você pode simplesmente ligar para:

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

4
Mesmo que a entrada contenha nulos, eles serão preservados (como delimitadores de registro).
21416 Toby Speight

6
Isso não carregará toda a entrada se não houver nulos? Nesse caso, o processamento de um arquivo com vários gigabytes pode estar com falha.
Ruslan

3
@Ruslan, sim, ele carrega toda a entrada. Esta solução não é uma boa ideia para arquivos com vários gigabytes.
JJoao

7
Esta é seriamente a melhor resposta. As outras expressões são muito distorcidas para serem lembradas. @JJoao Você pode usá-lo com -u, --unbuffered. O manmage declara: "carregue quantidades mínimas de dados dos arquivos de entrada e limpe os buffers de saída com mais frequência".
Not2qubit

tão. Muito de. isto.
sjas

85

A versão Perl funciona da maneira que você esperava.

perl -i -p -e 's/\n//' file

Como apontado nos comentários, vale a pena notar que isso é feito no local. -i.bakfornecerá um backup do arquivo original antes da substituição, caso sua expressão regular não seja tão inteligente quanto você pensava.


23
Por favor, mencione pelo menos que -isem sufixo não há backup . -i.bakprotege você de um erro fácil e feio (por exemplo, esquecendo-se de digitar -pe zerar o arquivo).
Telemachus

6
@Telemachus: É um ponto justo, mas pode ser discutido de qualquer maneira. A principal razão pela qual não mencionei é que o exemplo sed na pergunta do OP não faz backups, então parece supérfluo aqui. A outra razão é que nunca usei a funcionalidade de backup (acho os backups automáticos irritantes, na verdade), então sempre esqueço que ela está lá. A terceira razão é que torna minha linha de comando quatro caracteres mais longa. Para melhor ou pior (provavelmente pior), sou um minimalista compulsivo; Eu apenas prefiro concisão. Eu sei que você não concorda. Vou tentar o meu melhor para lembrar de avisar sobre backups no futuro.
Ire_and_curses

6
@Ire_and_curses: Na verdade, você acabou de fazer um argumento muito bom por me ignorar. Ou seja, você tem razões para suas escolhas e, se eu concordo ou não com as escolhas, eu certamente respeito isso. Não sei muito bem por que, mas ultimamente estou chorando sobre essa coisa em particular (a -ibandeira em Perl sem sufixo). Tenho certeza de que vou encontrar outra coisa para obcecar em breve. :)
Telêmaco

É realmente lamentável que isso não funcione com o stdin, especificando o -nome do arquivo. Existe uma maneira de fazer isso? Essa é a minha maneira de não me preocupar em modificar um arquivo usando um pipeline que começa com cat.
Steven Lu

O @StevenLu Perl lê o STDIN por padrão, se nenhum nome de arquivo for fornecido. Então você poderia fazer, por exemplo,perl -i -p -e 's/\n//' < infile > outfile
ire_and_curses

44

Quem precisa sed? Aqui está o bashcaminho:

cat test.txt |  while read line; do echo -n "$line "; done

2
Upvote, eu normalmente usava a resposta principal, mas ao canalizar / dev / urandom através dela, o sed não será impresso até o EOF e ^ C não é EOF. Essa solução é impressa toda vez que vê uma nova linha. Exatamente o que eu precisava! Obrigado!
Vasiliy Sharapov

1
então porque não: echo -n `cat days.txt` Deste post
Tony

9
@Tony porque backticks estão obsoletos e que o gato é redundante ;-) Use: echo $ (<days.txt)
seumasmac

10
Mesmo sem usar cat: while read line; do echo -n "$line "; done < test.txt. Pode ser útil se um sub-shell for um problema.
Carlo Cannas

5
echo $(<file)espreme todo o espaço em branco para um único espaço, não apenas as novas linhas: isso vai além do que o OP está pedindo.
Glenn Jackman

27

Para substituir todas as novas linhas por espaços usando o awk, sem ler o arquivo inteiro na memória:

awk '{printf "%s ", $0}' inputfile

Se você deseja uma nova linha final:

awk '{printf "%s ", $0} END {printf "\n"}' inputfile

Você pode usar um caractere diferente do espaço:

awk '{printf "%s|", $0} END {printf "\n"}' inputfile

END{ print ""}é uma alternativa mais curta para uma nova linha à direita.
Isaac

22
tr '\n' ' ' 

é o comando

Simples e fácil de usar.


14
ou simplesmente tr -d '\n'se você não quiser adicionar um espaço
spuder

21

Três coisas.

  1. tr(ou catetc.) não é absolutamente necessário. (GNU) sede (GNU) awk, quando combinados, podem fazer 99,9% de qualquer processamento de texto necessário.

  2. stream! = baseado em linha. edé um editor baseado em linhas. sednão é. Veja sed lecture para mais informações sobre a diferença. A maioria das pessoas confunde sedser baseada em linhas porque, por padrão, não é muito gananciosa na correspondência de padrões para correspondências SIMPLES - por exemplo, ao pesquisar e substituir por um ou dois caracteres, por padrão, ela substitui apenas na primeira correspondência encontra (a menos que especificado de outra forma pelo comando global). Nem haveria um comando global se fosse baseado em linhas e não em STREAM, porque avaliaria apenas linhas de cada vez. Tente correr ed; você notará a diferença. edé bastante útil se você deseja iterar sobre linhas específicas (como em um loop for), mas na maioria das vezes você deseja apenas sed.

  3. Dito isto,

    sed -e '{:q;N;s/\n/ /g;t q}' file
    

    funciona bem no GNU sedversão 4.2.1. O comando acima substituirá todas as novas linhas por espaços. É feio e um pouco complicado de digitar, mas funciona muito bem. Os {}podem ser deixados de fora, pois são incluídos apenas por razões de sanidade.


3
Como uma pessoa que sabe apenas o suficiente sedpara fazer coisas básicas, devo dizer que é mais do que você pode fazer, sedmas como é fácil entender o que está acontecendo. Eu tenho muita dificuldade em trabalhar, sedentão prefiro um comando mais simples quando posso usá-lo.
Nate

Usando t qcomo salto condicional, isso funciona com um padrão como s/\n / /(para unir todas as linhas que começam com um espaço) sem ler o arquivo inteiro na memória. Útil ao transformar arquivos com vários megabytes.
textshell

O artigo que você vinculou não reflete o que está dizendo
hek2mgl

Isso é quase 800 vezes mais lento que a resposta aceita em entradas grandes. Isso ocorre devido à substituição de cada linha de entrada cada vez maior.
Thor

13

A resposta com o: um rótulo ...

Como posso substituir uma nova linha (\ n) usando sed?

... não funciona no freebsd 7.2 na linha de comando:

(eco foo; barra de eco) | sed ': a; N; $! ba; s / \ n / / g'
sed: 1: ": a; N; $! ba; s / \ n / / g": rótulo não utilizado 'a; N; $! ba; s / \ n / / g'
foo
Barra

Mas, se você colocar o script sed em um arquivo ou usar -e para "construir" o script sed ...

> (eco foo; barra de eco) | sed -e: a -e N -e '$! ba' -e 's / \ n / / g'
foo bar

ou ...

> cat > x.sed << eof
:a
N
$!ba
s/\n/ /g
eof

> (echo foo; echo bar) | sed -f x.sed
foo bar

Talvez o sed no OS X seja semelhante.


A série de argumentos -e funcionou para mim no Windows usando o MKS! Obrigado!
JamesG

12

Solução fácil de entender

Eu tive esse problema. O ponto principal foi que eu precisava da solução para trabalhar com BSD (Mac OS X) e GNU (Linux e Cygwin ) sede tr:

$ echo 'foo
bar
baz


foo2
bar2
baz2' \
| tr '\n' '\000' \
| sed 's:\x00\x00.*:\n:g' \
| tr '\000' '\n'

Resultado:

foo
bar
baz

(tem nova linha à direita)

Funciona no Linux, OS X e BSD - mesmo sem suporte UTF-8 ou com um terminal de baixa qualidade.

  1. Use trpara trocar a nova linha por outro caractere.

    NULL( \000ou \x00) é bom porque não precisa de suporte UTF-8 e provavelmente não será usado.

  2. Use sedpara combinar com oNULL

  3. Use trpara trocar de volta novas linhas extras, se você precisar delas


1
Uma observação sutil na nomenclatura: o caractere \000é geralmente chamado de NUL(um L) e NULLé geralmente usado quando se fala de um ponteiro zero (em C / C ++).
Sqweek 29/02


9

Eu não sou especialista, mas acho sedque primeiro você deve acrescentar a próxima linha no espaço do padrão, usando " N". Na seção "Multiline Pattern Space" em "Advanced sed Commands" do livro sed & awk (Dale Dougherty e Arnold Robbins; O'Reilly 1997; página 107 na visualização ):

O comando multiline Next (N) cria um espaço de padrão de múltiplas linhas lendo uma nova linha de entrada e anexando-a ao conteúdo do espaço de padrão. O conteúdo original do espaço do padrão e a nova linha de entrada são separados por uma nova linha. O caractere de nova linha incorporado pode ser correspondido em padrões pela sequência de escape "\ n". Em um espaço de padrão de múltiplas linhas, o metacaractere "^" corresponde ao primeiro caractere do espaço de padrão, e não ao (s) caractere (s) após qualquer (s) nova (s) linha (s) incorporada (s). Da mesma forma, "$" corresponde apenas à nova linha final no espaço padrão e não a nenhuma nova linha incorporada. Depois que o comando Next é executado, o controle é passado para os comandos subseqüentes no script.

De man sed:

[2addr] N

Anexe a próxima linha de entrada ao espaço do padrão, usando um caractere de nova linha incorporado para separar o material anexado do conteúdo original. Observe que o número da linha atual é alterado.

Eu usei isso para pesquisar (vários) arquivos de log mal formatados, nos quais a string de pesquisa pode ser encontrada em uma próxima linha "órfã".


7

Usei uma abordagem híbrida para contornar a questão da nova linha, usando tr para substituir novas linhas por guias e, em seguida, substituindo as guias pelo que eu quiser. Nesse caso, "
" desde que estou tentando gerar quebras de HTML.

echo -e "a\nb\nc\n" |tr '\n' '\t' | sed 's/\t/ <br> /g'`

6

Em resposta à solução "tr" acima, no Windows (provavelmente usando a versão Gnuwin32 de tr), a solução proposta:

tr '\n' ' ' < input

não estava funcionando para mim, ele erro ou substituía o \ nw / '' por algum motivo.

Usando outro recurso de tr, a opção "delete" -d funcionou:

tr -d '\n' < input

ou '\ r \ n' em vez de '\ n'


3
No Windows, você provavelmente precisará usar tr "\n" " " < input. O shell do Windows (cmd.exe) não trata o apóstrofo como um caractere de citação.
Keith Thompson

Não, no Windows 10 subsistema Ubuntu, você precisa usartr "\n\r" " " < input.txt > output.txt
user1491819

Isso funciona em Windows 10 usando GnuWin32: cat SourceFile.txt | tr --delete '\r\n' > OutputFile.txt. Ou, em vez do Gnuwin32, use Gow (Gnu no Windows), github.com/bmatzelle/gow/wiki
Alchemistmatt

5

Solução à prova de balas. Seguro de dados binários e compatível com POSIX, mas lento.

O POSIX sed requer entrada de acordo com o arquivo de texto POSIX e as definições de linha POSIX , portanto, bytes nulos e linhas muito longas não são permitidas e cada linha deve terminar com uma nova linha (incluindo a última linha). Isso dificulta o uso do sed para o processamento de dados de entrada arbitrários.

A solução a seguir evita sed e, em vez disso, converte os bytes de entrada em códigos octais e depois em bytes novamente, mas intercepta o código octal 012 (nova linha) e gera a string de substituição no lugar dele. Tanto quanto posso dizer, a solução é compatível com POSIX, portanto deve funcionar em uma ampla variedade de plataformas.

od -A n -t o1 -v | tr ' \t' '\n\n' | grep . |
  while read x; do [ "0$x" -eq 012 ] && printf '<br>\n' || printf "\\$x"; done

Documentação de referência do POSIX: sh , linguagem de comandos do shell , od , tr , grep , read , [ , printf .

Ambos read, [e printfestão embutidos em pelo menos bash, mas que provavelmente não é garantida por POSIX, então em algumas plataformas pode ser que cada byte de entrada começará um ou mais novos processos, que vai retardar as coisas. Mesmo no bash, essa solução atinge apenas cerca de 50 kB / s, por isso não é adequada para arquivos grandes.

Testado no Ubuntu (bash, dash e busybox), FreeBSD e OpenBSD.


5

Em algumas situações, talvez você possa mudar RSpara outra string ou caractere. Dessa forma, \ n está disponível para sub / gsub:

$ gawk 'BEGIN {RS="dn" } {gsub("\n"," ") ;print $0 }' file

O poder do script de shell é que, se você não souber como fazê-lo de uma maneira, poderá fazê-lo de outra maneira. E muitas vezes você tem mais coisas a considerar do que criar uma solução complexa para um problema simples.

Com relação ao que o gawk é lento ... e lê o arquivo na memória, eu não sei disso, mas para mim o gawk parece funcionar com uma linha ao mesmo tempo e é muito muito rápido (não tão rápido quanto alguns dos outros , mas o tempo para escrever e testar também conta).

Eu processo MB e até GB de dados, e o único limite que encontrei é o tamanho da linha.


5

Se você tiver a infelicidade de ter que lidar com as terminações de linha do Windows, precisará remover \ro\n

tr '[\r\n]' ' ' < $input > $output

Isso substitui [por um espaço e \rpor um espaço e \npor um espaço e ]por um espaço. tr -d '\r\n' <fileremoveria qualquer um \rou \ncaracteres, mas também não é isso que está sendo solicitado. tr -d '\r' <fileremoverá quaisquer \rcaracteres (independentemente de serem adjacentes \n) que provavelmente estão mais próximos de serem úteis e possivelmente corretos para a necessidade do OP (ainda assumindo que você trentende essa notação de barra invertida).
Tripleee

4

Você pode usar xargs- ele será substituído \npor um espaço por padrão.

No entanto, haveria problemas se sua entrada apresentasse algum caso de unterminated quote, por exemplo, se os sinais de cotação em uma determinada linha não coincidirem.


O xargs também lida bem com a última linha:
AAAfarmclub

4

Localiza e substitui usando permitir \ n

sed -ie -z 's/Marker\n/# Marker Comment\nMarker\n/g' myfile.txt

Marcador

Torna-se

# Marcador Comentário

Marcador


4

Por que não encontrei uma solução simples awk?

awk '{printf $0}' file

printf imprimirá todas as linhas sem novas linhas, se você quiser separar as linhas originais com um espaço ou outro:

awk '{printf $0 " "}' file

echo "1\n2\n3" | awk '{printf $0}', isso funciona para mim. @ edi9999
Itachi

Você está certo, desculpe, eu esqueci o fem printf
edi9999 19/03

essa foi a única abordagem que funcionou para mim no git bash para windows
Platão

3

No Mac OS X (usando o FreeBSD sed):

# replace each newline with a space
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g; ta'
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g' -e ta

3

Para remover linhas vazias:

sed -n "s/^$//;t;p;"

Isto é para GNU Sed. No Sed normal, isso dá sed: 1: "s/^$//;t;p;": undefined label ';p;'.
Léo Léopold Hertz,

3

Usando o Awk:

awk "BEGIN { o=\"\" }  { o=o \" \" \$0 }  END { print o; }"

2
Você não precisa escapar das aspas e cifrão se alterar as exteriores para aspas simples. A letra "o" é geralmente considerada uma má escolha como nome de variável, pois pode ser confundida com o dígito "0". Você também não precisa inicializar sua variável, o padrão é uma cadeia nula. No entanto, se você não quer um espaço à esquerda estranha: awk '{s = s sp $0; sp = " "} END {print s}'. No entanto, veja minha resposta sobre uma maneira de usar o awk sem ler o arquivo inteiro na memória.
Pausado até novo aviso.

Por favor, verifique a resposta de Thor . É a maneira mais eficiente, de fácil leitura e apenas melhor por todos os meios a comparação desta abordagem (mesmo que isso iria funcionar)!
mschilli

Cara, eu entendi. Não há necessidade de esfregar na minha cara :-) A resposta de Thor está bem acima na página (o que é certo), então o que você se importa?
kralyk

3

Uma solução que eu particularmente gosto é anexar todo o arquivo no espaço de espera e substituir todas as novas linhas no final do arquivo:

$ (echo foo; echo bar) | sed -n 'H;${x;s/\n//g;p;}'
foobar

No entanto, alguém me disse que o espaço de espera pode ser finito em algumas implementações sed.


1
a substituição por uma sequência vazia em sua resposta oculta o fato de que sempre usar H para acrescentar ao espaço de espera significa que o espaço de espera começará com uma nova linha. Para evitar isso, você precisa usar1h;2,$H;${x;s/\n/x/g;p}
Jeff

3

Substitua as novas linhas por qualquer sequência e substitua a última nova linha também

As trsoluções puras só podem ser substituídas por um único caractere e as sedsoluções puras não substituem a última nova linha da entrada. A solução a seguir corrige esses problemas e parece ser segura para dados binários (mesmo com um código de idioma UTF-8):

printf '1\n2\n3\n' |
  sed 's/%/%p/g;s/@/%a/g' | tr '\n' @ | sed 's/@/<br>/g;s/%a/@/g;s/%p/%/g'

Resultado:

1<br>2<br>3<br>

Isso é ruim porque ele vai produzir uma saída indesejada em qualquer entrada contendo@
Steven Lu

@StevenLu: Não, @a entrada está OK. É escapado para %ae voltar novamente. A solução pode não ser completamente compatível com POSIX (no entanto, bytes nulos não são permitidos, portanto, não são bons para dados binários, e todas as linhas devem terminar com nova linha para que a trsaída não seja realmente válida).
Håkon A. Hjortland

Ah Vejo que você consertou. Meio complicado para o que deveria ser uma operação simples, mas um bom trabalho.
Steven Lu

3

É sed que introduz as novas linhas após a substituição "normal". Primeiro, ele apara o caractere de nova linha, depois processa de acordo com suas instruções e, em seguida, introduz uma nova linha.

Usando sed, você pode substituir "o fim" de uma linha (não o caractere de nova linha) após ser cortado, com uma sequência de sua escolha, para cada linha de entrada; mas, o sed produzirá linhas diferentes. Por exemplo, suponha que você queira substituir o "fim de linha" por "===" (mais geral que a substituição por um único espaço):

PROMPT~$ cat <<EOF |sed 's/$/===/g'
first line
second line
3rd line
EOF

first line===
second line===
3rd line===
PROMPT~$

Para substituir o caractere de nova linha pela string, você pode, ineficientemente, usar tr , como apontado anteriormente, para substituir os caracteres de nova linha por um "caractere especial" e, em seguida, usar sed para substituir esse caractere especial pela string que você deseja .

Por exemplo:

PROMPT~$ cat <<EOF | tr '\n' $'\x01'|sed -e 's/\x01/===/g'
first line
second line
3rd line
EOF

first line===second line===3rd line===PROMPT~$

3

Você também pode usar esse método

sed 'x;G;1!h;s/\n/ /g;$!d'

Explicação

x   - which is used to exchange the data from both space (pattern and hold).
G   - which is used to append the data from hold space to pattern space.
h   - which is used to copy the pattern space to hold space.
1!h - During first line won't copy pattern space to hold space due to \n is
      available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
      last line.

Fluxo:
Quando a primeira linha sai da entrada, a troca é feita, então 1 vai para o espaço de retenção e \ n chega ao espaço do padrão, acrescentando o espaço de retenção ao espaço do padrão e, em seguida, a substituição é executada e exclui o espaço do padrão.
Durante a segunda troca de linha, 2 vai para o espaço de espera e 1 para o espaço do padrão, Ganexa o espaço de espera no espaço do padrão, hcopia o padrão para ele e a substituição é feita e excluída. Esta operação continua até que eof seja alcançado e imprima o resultado exato.


No entanto, esteja avisado de que isso echo 'Y' | sed 'x;G;1!h;s/\n/X/g;$!d'resulta em XY.
Spooky

3

Outro método GNU sed , quase o mesmo que a resposta de Zsolt Botykai , mas usa sedo comando menos usado y( transliterado ), que salva um byte de código (o final g):

sed ':a;N;$!ba;y/\n/ /'

Seria de esperar yque funcionasse mais rápido do que s(talvez em trvelocidades 20x mais rápidas), mas no GNU sed v4.2.2 y é cerca de 4% mais lento que s.


Versão BSD mais portátil sed:

sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'

2
Com o BSD sed yé cerca de 15% mais rápido. Veja esta resposta para um exemplo de trabalho.
Thor

Além disso, com o BSD sed, os comandos sed precisam terminar após um rótulo, assim sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'seria o caminho a percorrer.
ghoti 13/09/18
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.