Tenho um arquivo de texto com o seguinte formato. A primeira linha é a "CHAVE" e a segunda linha é o "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
Preciso do valor na mesma linha da chave. Portanto, a saída deve ser semelhante a esta ...
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
Seria melhor se eu pudesse usar algum delimitador como $
ou ,
:
KEY 4048:1736 string , 3
Como faço para mesclar duas linhas em uma?
pr
,paste
,awk
,xargs
,sed
epure bash
! (xargs
é o mais lento, mais lento que o bash !)Respostas:
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
fonte
printf
strings de expansão como%s
forem encontradas dentro$0
. Essa falha pode ser evitada assim:'NR%2{printf "%s ",$0;next;}1'
1
depois da chave de fechamento?paste
é bom para este trabalho:paste -d " " - - < filename
fonte
paste
, por outro lado, se comporta perfeitamente. +1.cut
mas sempre esqueçopaste
. É demais para esse problema. Eu precisava combinar todas as linhas de stdin e fiz isso facilmente compaste -sd ' ' -
.-
significa stdin, entãopaste - -
significa ler de stdin, então ler de stdin, você pode empilhar quantos deles quiser, eu espero.Alternativa para sed, awk, grep:
xargs -n2 -d'\n'
Isso é melhor quando você deseja unir N linhas e precisa apenas de saída delimitada por espaço.
Minha resposta original foi
xargs -n2
que separa em palavras ao invés de linhas.-d
pode ser usado para dividir a entrada por qualquer caractere único.fonte
-d '\n'
xargs
usuário regular, mas não sabia disso. Ótima dica.Existem mais maneiras de matar um cachorro do que enforcamento. [1]
awk '{key=$0; getline; print key ", " $0;}'
Coloque o delimitador que desejar entre as aspas.
Referências:
fonte
$0
. Ogetline
comando também pega "a próxima" linha de entrada e a coloca$0
. Assim, a primeira instrução pega a primeira linha e o comando print concatena o que foi salvo na variávelkey
com uma string contendo uma vírgula, junto com a linha que foi buscada usandogetline
. Mais claro? :)Aqui está minha solução no bash:
while read line1; do read line2; echo "$line1, $line2"; done < data.txt
fonte
Embora pareça que as soluções anteriores funcionariam, se uma única anomalia ocorrer no documento, a saída será fragmentada. Abaixo está um pouco mais seguro.
sed -n '/KEY/{ N s/\n/ /p }' somefile.txt
fonte
/KEY/
fazer? O que op
faz no final?/KEY/
pesquisas pela linha com oKEY
. op
imprime o resultado. é mais seguro porque só aplica a operação em linhas com umKEY
nele.Aqui está outra maneira com
awk
:awk 'ORS=NR%2?FS:RS' file
$ 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
Conforme indicado por Ed Morton nos comentários, é melhor adicionar chaves para segurança e parênteses para portabilidade.
awk '{ ORS = (NR%2 ? FS : RS) } 1' file
ORS
significa separador de registro de saída. O que estamos fazendo aqui é testar uma condição usando oNR
que armazena o número da linha. Se o módulo deNR
é um valor verdadeiro (> 0), então definimos o separador de campo de saída para o valor deFS
(separador de campo), que por padrão é espaço, caso contrário atribuímos o valor deRS
(Separador de Registro), que é nova linha.Se você deseja adicionar
,
como separador, use o seguinte:awk '{ ORS = (NR%2 ? "," : RS) } 1' file
fonte
ORS
e está sendo tratado comotrue
uma vez que ORS obtém um valor diferente de zero ou uma string nula e desperta a suposição correta de que deveria ser uma comparação em vez de uma comparação numérica? É outra coisa? Eu realmente não tenho certeza e então o teria escrito comoawk '{ORS=(NR%2?FS:RS)}1' file
. Coloquei entre parênteses a expressão ternária para garantir a portabilidade também."ex" é um editor de linha programável que está na mesma família que 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
Isso diz que para cada linha, se ela corresponder a "KEY", execute um j oin da linha seguinte. Depois que completa comando (contra todas as linhas), emitir um w rito e q uit.
fonte
Se Perl for uma opção, você pode tentar:
perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt
fonte
-0
perl para definir o separador de registros ($/)
para nulo, para que possamos abranger várias linhas em nosso padrão de correspondência. As páginas de manual são um pouco técnicas demais para eu descobrir o que isso significa na prática.Você pode usar o awk assim para combinar 2 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
fonte
Outras soluções usando o vim (apenas para referência).
Solução 1 :
Abra o arquivo no vim
vim filename
e execute o comando:% normal Jj
Este comando é muito fácil de entender:
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
.fonte
norm!
mais robusto quenormal
no caso deJ
ser remapeado. +1 para solução vim.Você também pode usar o seguinte comando vi:
fonte
:%g//j
porque tudo que você precisa é uma correspondência para que a junção seja executada, e uma string nula ainda é uma regex válida.//
, o padrão de pesquisa anterior será usado. Se não houver um padrão anterior, o Vim simplesmente relata um erro e não faz nada. A solução de Jdamian funciona o tempo todo.Uma ligeira variação na resposta de glenn jackman usando
paste
: se o valor da-d
opção delimitador contém mais de um caractere,paste
alterna entre os caracteres um por um e, combinado com as-s
opções, continua fazendo isso enquanto processa o mesmo arquivo de entrada.Isso significa que podemos usar o que quisermos como separador mais a sequência de escape
\n
para mesclar duas linhas por 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 um bônus, se o
paste
for compatível com POSIX, isso não modificará a nova linha da última linha no arquivo, portanto, para um arquivo de entrada com um número ímpar de linhas comopaste
não adicionará o caractere de separação na última linha:$ paste -s -d ',\n' infile KEY 4048:1736 string,3 KEY 0:1772 string
fonte
nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename
Isso é lido 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
fonte
No caso em que precisei combinar duas linhas (para facilitar o processamento), mas permitir que os dados passassem do específico, achei isso útil
data.txt
cat data.txt | nawk '$0 ~ /string1=/ { printf "%s ", $0; getline; printf "%s\n", $0; getline } { print }' > converted_data.txt
a saída então se parece com:
convertido_data.txt
fonte
Outra abordagem usando o vim seria:
Isso se aplica a
join
(à linha abaixo) a todas as linhas que contêm a palavraKEY
. Resultado:fonte
A maneira mais simples é aqui:
sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2
fonte
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt
-0
engole todo o arquivo em vez de lê-lo linha por linha;pE
envolve o código com loop e imprime a saída, consulte os detalhes em http://perldoc.perl.org/perlrun.html ;^KEY
corresponder a "KEY" no início da linha, seguido por correspondência não gananciosa de qualquer coisa (.*?
) antes da sequência de\s+
de qualquer tipo, incluindo quebras de linha;(\d+)
que capturamos e reinserimos posteriormente como$1
;seguido pelo fim da linha
$
.\K
exclui convenientemente tudo em seu lado esquerdo da substituição, então{ $1}
substitui apenas a sequência 1-2, consulte http://perldoc.perl.org/perlre.html .fonte
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 remediado. Este exemplo é onde a linha "chave" terminou em: e nenhuma outra linha terminou.
#!/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
fonte
Experimente a seguinte linha:
while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file
Coloque o delimitador no meio
"$line1 $line2";
por exemplo, se o delimitador for
|
, então:"$line1|$line2";
fonte
Você pode usar
xargs
assim:fonte
xargs -n 2
mas esta resposta não explica isso de forma alguma.