Remova o caractere de nova linha a cada N linhas

13

Processando o texto, preciso remover o caractere de nova linha a cada duas linhas.

Exemplo de texto:

this is line one
and this is line two
the third and the
fourth must be pasted too

Saída desejada:

this is line one and this is line two
the third and the fourth must be pasted too

Eu tentei um loop while , mas um loop while é uma prática ruim. É possível fazer isso usando tr ou qualquer outro comando?

    
por jomaweb 19.08.2016 / 08:52

10 respostas

21

paste (também um utilitário simples padrão POSIX como tr ) é sua ferramenta para isso.

Supondo que você queira que esses caracteres de nova linha sejam substituídos por um espaço, em vez de apenas removidos , como em sua amostra:

paste -d ' ' - - < file

Ou:

paste -sd ' \n' file

Substitua ' ' por 'paste' se você realmente quiser removê-los.

Para substituir 2 de 3:

paste -sd '  \n' file

1 de 3, começando com o segundo:

paste -sd '\n \n' file

E assim por diante.

Outra coisa boa com tr -d '\n' < file é que não deixará uma linha não terminada. Por exemplo, se você remover todas as novas linhas em um arquivo (como em tr '\n' ' ' < file ou paste ), você não terá mais nenhuma linha, pois as linhas precisam ser terminadas com um caractere de nova linha. Portanto, geralmente é melhor usar paste -sd 'paste -sd ' ' file' file para isso (como em %code% ou %code% ), o que adicionará esse caractere de nova linha à direita necessário para ter um texto válido.

    
por 19.08.2016 / 09:31
11

Com o moderno GNU sed

sed -rz 's/\n([^\n]*\n)/ /g' sample.text

E awk

awk '{getline line2;print $0, line2}' sample.text
    
por 19.08.2016 / 09:50
6

Use sed para isso, conforme mostrado abaixo:

SHW@SHW:/tmp $ cat a
this is line one
and this is line two
the third and the
fourth must be pasted too

SHW@SHW:/tmp $ sed 'N;s/\n/ /' a -i

SHW@SHW:/tmp $ cat a
this is line one and this is line two
the third and the fourth must be pasted too
    
por 19.08.2016 / 09:04
4

Outra maneira é usar xargs :

$ < txt xargs -d '\n' -n 2 echo
this is line one and this is line two
the third and the fourth must be pasted too

onde

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Embora, esta solução seja bastante excessiva porque um processo echo é executado para cada linha ... Assim, além de exemplos de brinquedo, uma solução baseada em awk / sed ou similar deve ser preferida.

    
por 19.08.2016 / 09:35
3
$ awk '{printf "%s%s",$0,(NR%2?" ":"\n")}' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Isso imprime cada linha, $0 , seguida por um espaço ou uma nova linha, dependendo de o número da linha, NR , ser ímpar ou par.

A expressão NR%2?" ":"\n" é uma declaração ternária. A expressão NR%2 é avaliada como verdadeira (diferente de zero) se o número da linha for ímpar. No caso, a expressão ternária retorna um espaço. Se for avaliado como falso (zero), a nova linha será retornada.

Alternativa

Como sugerido por Costas nos comentários:

$ awk '{ORS=(NR%2?" ":RS)}1' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Aqui, a instrução ternary NR%2?" ":RS é usada para retornar um espaço ou o separador de registro de entrada ( RS , default = newline). Este valor é atribuído ao separador de registro de saída, ORS . O 1 no final do comando é a abreviação enigmática do awk para impressão-registro.

    
por 19.08.2016 / 09:13
3

Isso é extremamente simples no vim. Para unir todas as linhas, use o comando J e use o comando %norm para aplicá-lo a todas as linhas simultaneamente. Por exemplo

:%norm J<CR>

(Apenas no caso de você não estar familiarizado com o vim, <CR> significa apenas entrar)

Isso funciona até mesmo para unir um número arbitrário de linhas. Por exemplo, juntar-se a cada dez linhas seria

:%norm 9J<CR>

Se você não estiver à vontade com o vim e preferir usá-lo como uma ferramenta de linha de comando, em vez de usar um editor de texto interativo, poderá fazer isso:

vim myfile -c '%norm J' -c 'wq'
    
por 19.08.2016 / 15:33
2

Solução genérica, substitua 5 pelo número de linhas necessárias

$ # eof to ensure last line has newline ending
$ seq 16 | perl -pe 's/\n/ / if ++$i%5 && !eof'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

$ # or just use pr
$ seq 16 | pr -5ats' '
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16
    
por 19.08.2016 / 10:01
1

Você pode usar awk para isso:

$ awk '{c="\n"} NR%2 {c=" "} { printf("%s%s", $0, c) } ' txt

Produz:

this is line one and this is line two
the third and the fourth must be pasted too

onde:

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

As ações awk são executadas para cada linha, a variável especial $0 faz referência à linha atual, NR é o número da linha atual (começando em 1). A segunda ação é guardada pela expressão NR%2 , que é a operação do módulo. Assim, c=" " só é executado se NR%2 for verdadeiro, ou seja, para números de linha ímpares.

A sintaxe awk é semelhante a C, mas alguns elementos são opcionais em alguns contextos - por exemplo, ponto e vírgula.

    
por 19.08.2016 / 09:11
0

Usando ed :

$ cat text
this is line one
and this is line two
the third and the
fourth must be pasted too
this is line one
and this is line two
the third and the
fourth must be pasted too

$ ed text <<'END_ED'
g/./s/$/ /\
j
w text.new
END_ED
164
164

$ cat text.new
this is line one and this is line two
the third and the fourth must be pasted too
this is line one and this is line two
the third and the fourth must be pasted too

Os comandos de edição ed serão, para cada linha ( g aplica um conjunto de comandos de edição a cada linha correspondente à expressão regular dada), adicionar um caractere de espaço ao final e associá-lo à próxima linha. Em seguida, grava o texto resultante em um arquivo chamado text.new .

    
por 19.08.2016 / 10:29
0

com o Ruby.

Eu assumo que cada bloco de n linhas deve ser unido. Suponha que n = 3 , o arquivo de entrada seja 'infile' e os resultados sejam gravados no arquivo 'outfile' .

Construa um arquivo

Ruby -e "File.write 'infile', <<_
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> Line 6
> Line 7
> _"

Confirme o conteúdo do arquivo

ruby -e "p File.read 'infile'"
  # "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\n"

Remover novas linhas e gravar no arquivo

ruby -e "File.write 'outfile', File.readlines('infile').
  each_with_index { |line,i| line.chomp! unless (i+1)%3==0 }"

Confirme os conteúdos

ruby -e "puts File.read 'outfile'"
  # ["Line 1", "Line 2", "Line 3\n", "Line 4", "Line 5", "Line 6\n", "Line 7"]
    
por 20.08.2016 / 22:42