Como mesclar todas as duas linhas em uma a partir da linha de comando?

Eu tenho um arquivo de texto com o seguinte formato. A primeira linha é a “CHAVE” e a segunda linha é a “VALOR”.

KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Eu preciso do valor na mesma linha da chave. Então a saída deve ficar assim …

 KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Será melhor se eu puder usar algum delimitador como $ ou:

 KEY 4048:1736 string , 3 

Como faço para mesclar duas linhas em uma?

awk:

 awk 'NR%2{printf "%s ",$0;next;}1' yourFile 

observe que há uma linha vazia no final da saída.

sed:

 sed 'N;s/\n/ /' yourFile 

paste é bom para este trabalho:

 paste -d " " - - < filename 

Há mais maneiras de matar um cachorro do que enforcá-lo. [1]

 awk '{key=$0; getline; print key ", " $0;}' 

Coloque o delimitador que você quiser dentro das aspas.


Referências:

  1. Originalmente “muitas maneiras de esfolar o gato”, reverteu para uma expressão mais antiga e potencialmente originária que também não tem nada a ver com animais de estimação.

Alternativa para sed, awk, grep:

 xargs -n2 -d'\n' 

Isso é melhor quando você deseja unir N linhas e só precisa de uma saída delimitada por espaço.

Minha resposta original era xargs -n2 que separa palavras e não linhas. -d pode ser usado para dividir a input por qualquer caractere único.

Aqui está a minha solução no bash:

 while read line1; do read line2; echo "$line1, $line2"; done < data.txt 

Embora pareça que as soluções anteriores funcionariam, se uma única anomalia ocorresse no documento, a saída se dividiria. Abaixo está um pouco mais seguro.

 sed -n '/KEY/{ N s/\n/ /p }' somefile.txt 

Aqui está outra maneira com o awk :

 awk 'ORS=NR%2?FS:RS' file 

 $ cat file KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

 $ awk 'ORS=NR%2?FS:RS' file KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Como indicado por Ed Morton nos comentários, é melhor adicionar chaves para segurança e parentes para portabilidade.

 awk '{ ORS = (NR%2 ? FS : RS) } 1' file 

ORS significa Output Record Separator. O que estamos fazendo aqui é testar uma condição usando o NR que armazena o número da linha. Se o módulo de NR é um valor verdadeiro (> 0) então configuramos o Output Field Separator para o valor de FS (Field Separator) que por padrão é espaço, senão nós designamos o valor de RS (Record Separator) que é newline.

Se você deseja adicionar , como o separador, use o seguinte:

 awk '{ ORS = (NR%2 ? "," : RS) } 1' file 

“ex” é um editor de linha de script que está na mesma família de sed, awk, grep, etc. Acho que pode ser o que você está procurando. Muitos clones / sucessores modernos do vi também possuem um modo vi.

  ex -c "%g/KEY/j" -c "wq" data.txt 

Isto diz para cada linha, se coincide com “KEY” executar um j oin da seguinte linha. Depois que o comando for concluído (contra todas as linhas), emita um wite e um q uit.

Se Perl é uma opção, você pode tentar:

 perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt 

Você pode usar o awk assim para combinar dois pares de linhas:

 awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \ END {if (length(line)) print line;}' flle 

Você também pode usar o seguinte comando vi:

 :%g/.*/j 
 nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename 

Isto lê como

 $0 ~ /string$/ ## matches any lines that end with the word string printf ## so print the first line without newline getline ## get the next line printf "%s\n" ## print the whole line and carriage return 

No caso em que eu precisava combinar duas linhas (para facilitar o processamento), mas permitir que os dados passassem do específico, achei isso útil

data.txt

 string1=x string2=y string3 string4 

cat data.txt | nawk ‘$ 0 ~ / string1 = / {printf “% s”, $ 0; Obter linha; printf “% s \ n”, $ 0; getline} {print} ‘> convert_data.txt

saída, em seguida, parece:

convert_data.txt

 string1=x string2=y string3 string4 

Outras soluções usando o vim (apenas para referência).

Solução 1 :

Abra o arquivo no vim filename arquivo vim vim filename e execute o comando :% normal Jj

Este comando é fácil de entender:

  • %: para todas as linhas,
  • normal: executa o comando normal
  • Jj: execute o comando Join e pule para abaixo da linha

Depois disso, salve o arquivo e saia com :wq

Solução 2 :

Execute o comando no shell, vim -c ":% normal Jj" filename , salve o arquivo e saia com :wq .

Uma pequena variação na resposta de glenn jackman usando paste : se o valor para a opção -d delimitador contiver mais de um caractere, o paste percorre os caracteres um por um, e combinado com as opções -s continua fazendo isso enquanto processa o mesmo arquivo de input .

Isso significa que podemos usar o que quisermos como o separador mais a seqüência de escape \n para mesclar duas linhas de cada vez.

Usando uma vírgula:

 $ paste -s -d ',\n' infile KEY 4048:1736 string,3 KEY 0:1772 string,1 KEY 4192:1349 string,1 KEY 7329:2407 string,2 KEY 0:1774 string,1 

e o cifrão:

 $ paste -s -d '$\n' infile KEY 4048:1736 string$3 KEY 0:1772 string$1 KEY 4192:1349 string$1 KEY 7329:2407 string$2 KEY 0:1774 string$1 

O que isso não pode fazer é usar um separador que consiste em vários caracteres.

Como bônus, se a paste for compatível com POSIX, isso não modificará a nova linha da última linha no arquivo, portanto, para um arquivo de input com um número ímpar de linhas como

 KEY 4048:1736 string 3 KEY 0:1772 string 

paste não vai aderir ao caractere de separação na última linha:

 $ paste -s -d ',\n' infile KEY 4048:1736 string,3 KEY 0:1772 string 

O caminho mais simples está aqui:

  1. Remova linhas pares e escreva em algum arquivo temporário 1.
  2. Remover linhas ímpares e escrevê-lo em algum arquivo temporário 2.
  3. Combine dois arquivos em um usando o comando paste com -d (significa delete space)

 sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2 

 perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt 

-0 engole o arquivo inteiro em vez de lê-lo linha por linha;
pE quebra o código com loop e imprime a saída, veja detalhes em http://perldoc.perl.org/perlrun.html ;
^KEY match “KEY” no início da linha, seguido por correspondência não-gulosa de qualquer coisa ( .*? ) Antes da seqüência de

  1. um ou mais espaços \s+ de qualquer tipo, incluindo quebras de linha;
  2. um ou mais dígitos (\d+) que capturamos e depois reinserimos como $1 ;

seguido pelo final da linha $ .

\K convenientemente exclui tudo do lado esquerdo da substituição, de modo que { $1} substitui apenas a sequência 1-2; consulte http://perldoc.perl.org/perlre.html .

Uma solução mais geral (permite que mais de uma linha de acompanhamento seja unida) como um script de shell. Isso adiciona uma linha entre cada um, porque eu precisava de visibilidade, mas isso é facilmente corrigido. Este exemplo é onde a linha “chave” terminou em: e nenhuma outra linha fez.

 #!/bin/bash # # join "The rest of the story" when the first line of each story # matches $PATTERN # Nice for looking for specific changes in bart output # PATTERN='*:'; LINEOUT="" while read line; do case $line in $PATTERN) echo "" echo $LINEOUT LINEOUT="$line" ;; "") LINEOUT="" echo "" ;; *) LINEOUT="$LINEOUT $line" ;; esac done 

Tente a seguinte linha:

 while read line1; do read line2; echo "$line1 $line2"; done new_file 

Coloque o delimitador no meio

 "$line1 $line2"; 

por exemplo, se o delimitador for | , então:

 "$line1|$line2"; 

Você pode usar xargs como este:

 xargs -a file