transformar arquivo multi multi line para multi on line

1

Este é o arquivo que eu quero transformar:

john doe  
555-666-333  
[email protected]  
die  
jane doe  
Beverly Hills
444-333-111  
[email protected]  
die  

Eu quero que o arquivo resultante seja assim:

john doe,555-666-333,[email protected]  
jane doe,Beverly Hills,444-333-111,[email protected]

A palavra die está no arquivo e eu quero usá-lo para delimitar minhas linhas, assim elas não aparecerão na saída como mostrado acima. Eu tentei muitos scripts usando o awk, sed ou perl, mas não achei um caminho.
Eu também sou um iniciante no bash. Qualquer ajuda será apreciada.
Obrigado.

editar
Eu mudei o formato do arquivo. O formato anterior não leva em conta o fato de que o número de palavras entre die pode variar.

    
por Anicet Ebou 20.04.2018 / 14:23

4 respostas

4
$ awk -v OFS=',' '/^die$/ { print substr(lines,2); lines=""; next } { lines=lines OFS $0 }' file
john doe,555-666-333,[email protected]
jane doe,Beverly Hills,444-333-111,[email protected]

A mesma coisa se aplica aos dados que contêm vírgulas (veja o final da minha resposta abaixo). Se os dados contiverem vírgulas, convém usar isto:

awk -v OFS=',' '
    /^die$/ { print substr(lines,2); lines=""; next }
    /,/     { $0=sprintf("\"%s\"", $0 ) }
            { lines=lines OFS $0 }' file

O código cria uma string em lines , que é delimitada por OFS (uma vírgula). Quando a palavra die é encontrada sozinha em uma linha, a sequência em lines é gerada. Tho substr() call remove a vírgula que foi adicionada na frente da linha quando o primeiro campo do registro foi anexado à string. As linhas com vírgulas são tratadas da mesma maneira que no meu código abaixo.

Usando o GNU awk ou mawk , mas não o BSD awk , você também pode fazer

mawk -v RS='\ndie\n' -v FS='\n' -v ORS='\n' -v OFS=',' '{$1=$1;print}' file

Isso não produziria campos citados para dados que contenham vírgulas.

O $1=$1 força awk a reconfigurar o registro de acordo com as variáveis OFS (separador de campo de saída) e ORS (separador de registro de saída) antes da saída.

Responder antes de atualizar para pergunta:

paste -d, - - - - <file

Isso produziria

john doe,555-666-333,[email protected],die
jane doe,444-333-111,[email protected],die

Para remover as linhas die (estas são totalmente desnecessárias):

paste -d, - - - - <file | cut -d, -f 1-3

Os trabalhos acima funcionarão se os dados originais não contiverem vírgulas.

Você também pode filtrar as linhas die desde o início:

sed '/^die$/d' file | paste -d, - - -

Isso funcionaria mesmo se os dados originais contivessem vírgulas.

Se os dados contiverem vírgulas, convém pré-processá-lo para adicionar citações em torno dessas linhas:

awk '/^die$/ { next } /,/ { $0=sprintf("\"%s\"", $0 ) } 1' file | paste -d, - - -

Dado o arquivo

john doe
555-666-333
[email protected]
die
jane doe
444-333-111
[email protected]
die
Me, myself and I
000-000-000
[email protected]

esse último comando geraria

john doe,555-666-333,[email protected]
jane doe,444-333-111,[email protected]
"Me, myself and I",000-000-000,[email protected]
    
por 20.04.2018 / 14:30
2

Você pode fazer isso com um tipo idiomático de awk assim:

$ awk '$1=$1' RS='.die\n' OFS="," FS='\n' file1
john doe,555-666-333,[email protected]
jane doe,Beverly Hills,444-333-111,[email protected]

Acima de awk , definimos que o Record Separator RS é a entrada die que é usada em seu arquivo para separar os detalhes da pessoa.

$1=$1 força o awk a recalcular e imprimir os campos de entrada usando "," como Separador do campo de saída OFS

PS: quando suspeito de finais de arquivo incorretas, como \r i, uso para chamar tr para remover possíveis \r caracteres: tr -d '\r' file1 |awk .....

A propósito, você também usa sed assim:

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

Isso produzirá a mesma saída do awk, enganando o sed para usar o caractere nulo como separador de registro.

Assim que não houver caracteres nulos reais no arquivo de entrada, o sed tratará todo o arquivo de entrada como um grande registro == uma grande linha.

    
por 20.04.2018 / 15:20
0

Uma maneira é usar paste e sed :

paste -sd, <infile |sed 's/,die,\?/\n/g'
    
por 20.04.2018 / 19:50
0

Isso pode ser feito usando o editor "sed" de maneira POSIX

sed -e '
    :a
       $q;N;y/\n/,/
       s/,die$//;t
    ba
' input_file

Método:

  • Configure um loop e anexe a próxima linha ao espaço do padrão.
    • N comando
  • Altere a nova linha para vírgula e tente remover o "morrer"
    • Comandos y /// s ///
  • Caso você tenha sucesso, você está pronto e não faz mais nenhum processamento para isso.
    • Comando t sem um rótulo
  • Caso contrário, volte para mais e apenas no caso de estarmos eof, nós resgatamos.
    • Comandos b e q .

Também poderíamos usar o Perl para isso:

perl -lne '
    push @A, $_ unless /^die$/;
    print join ",", splice @A if /^die$/ || eof;
' input_file

Onde acumulamos linhas em um array até vermos a linha "die". Em que ponto nós juntamos o conteúdo do array por meio de vírgula (também esvaziamos o array).

Também podemos fazer slurp no arquivo e invocar o Perl para obter os resultados:

perl -lF'/^die\n/m' -0777nae 'print join ",", split /\n/ for @F' input_file
  • -F '/ ^ die \ n / m' irá dividir o arquivo slurped como uma string no regex BOL die seguido por uma nova linha.
  • -0 777 ativará o efeito slurping. -n deve desativar a cópia automática de linhas e -a dividirá as linhas com slur (no nosso caso apenas uma linha) com base no valor -F .
por 21.04.2018 / 07:17