Bash: Converter saída de várias linhas para linha única

1

Estou obtendo uma saída de várias linhas da seguinte forma:

Saída real:

GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU1,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0

No script Bash, preciso converter a saída acima em uma única linha, com valores em uma coluna delimitados por # símbolo e duas colunas separadas por uma vírgula.

Resultado esperado:

GenuineIntel#GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz#Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0#CPU1 and so on.

Como fazer isso?

    
por Mandar Shinde 08.08.2014 / 07:31

4 respostas

1

Se os dados de entrada estiverem em um arquivo chamado input :

$ awk -F, '/CPU0/{for (i=1; i<=NF;i++) {a[i]=$i};next} {for(i=1;i<=NF-1;i++){printf a[i]"#"$i","}; print a[NF]"#"$NF}' input
GenuineIntel#GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz#Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0#CPU1,64#64,EM64T Family 6 Model 45 Stepping 7#EM64T Family 6 Model 45 Stepping 7,(null)#(null),3093#3093,0#0

Tomando o comando awk uma parte de cada vez:

  • -F,

    Isso diz a awk para usar uma vírgula como separador de campo de entrada.

  • /CPU0/{for (i=1; i<=NF;i++) {a[i]=$i};next}

    Isso começa com /CPU0/ , que é um seletor de endereço que seleciona a primeira linha (a da CPU0). Para essa linha, armazena todos os valores de campo na matriz a . O comando next informa awk para pular para a próxima linha.

  • for(i=1;i<=NF-1;i++){printf a[i]"#"$i","}

    Isso diz awk para imprimir a coluna i para a primeira linha, seguida por um hashmark seguido pela coluna i da segunda linha, seguida por uma vírgula. Ele faz isso para todos os campos, exceto o último.

    Como printf é usado, nenhum caractere de nova linha é impresso.

  • print a[NF]"#"$NF}

    Isso diz a awk para imprimir o último campo da primeira linha seguido por um hashmark, seguido pelo último campo da segunda linha.

    Porque print é usado, o último caractere é impresso em uma nova linha, completando a saída.

por 08.08.2014 / 08:01
2

Aqui está uma abordagem mais geral de awk que não depende do conteúdo específico do arquivo:

awk -F, '{for(i=1;i<=NF;i++){a[NR][i]=$(i)}}
         END{
            for(i=1;i<NF;i++){printf "%s#%s,",a[1][i],a[2][i]} 
            print a[1][NF]"#"a[2][NF]
        }' file

Explicação

  • for(i=1;i<=NF;i++){a[NR][i]=$(i)} : itera pelos campos de cada linha (que são separados por vírgulas devido à -F, ) definindo a variável i para todos os valores de 1 para o número de campos ( NF ). NR é o número da linha atual que, no seu exemplo, será 1 ou 2. a[NR][i]=$(i) configura uma matriz bidimensional, uma matriz por linha e salva cada campo nela. Basicamente, a matriz a será semelhante a:

      1                        2                        3   
    1 1st field of 1st line    2nd field of 1st line    3rd field of 1st line
    2 1st field of 2nd line    2nd field of 2nd line    3rd field of 2nd line
    

    E assim por diante. Então, a[1][2] será o segundo campo da primeira linha.

  • END{} : isso é executado depois que o restante do arquivo é processado.

  • for(i=1;i<NF;i++){printf "%s#%s,",a[1][i],a[2][i]} : itera todos os campos e imprime o campo atual da primeira linha, um # e o campo correspondente da segunda linha.
  • print a[1][NF]"#"a[2][NF] : imprime o último campo (de cada linha). Isso é feito separadamente para que possamos imprimir os outros seguidos por uma vírgula, mas esta é seguida por uma nova linha.

E a mesma ideia em Perl:

perl -F, -ane 'chomp($F[$#F]);
               $k{$.}=\@F; 
               END{
                for($l=0;$l<$#F;$l++){
                  print "${$k{1}}[$l]#${$k{2}}[$l],"
                }
                print "${$k{1}}[$#F]#${$k{2}}[$#F]\n"}' file

Isso tem a vantagem de não depender de nenhum texto específico presente no arquivo. Ele funcionará para linhas arbitrárias de dados, desde que haja o mesmo número de campos separados por vírgulas em cada linha.

    
por 08.08.2014 / 16:08
1

perl golf:

perl -F, -lane'push@{$f[$_]},$F[$_]for 0..$#F}{$,=",";$"="#";print map"@{$_}",@f'
    
por 08.08.2014 / 17:33
0
$ cat /tmp/tmp     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU1,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU2,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0    
$ awk -F ',' 'BEGIN{ORS=" ";cpu=",";print "GenuineIntel,"} {gsub(/GenuineIntel/,"");for (i=1;i<=2;i++) {printf $i};print "#";cpu=cpu"#"$3 } END{sub(/,#/,",",cpu);print cpu}' /tmp/tmp
GenuineIntel,  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz #  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz #  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz # ,CPU0#CPU1#CPU2     

Não é perfeito, há um # extra na última linha, é na frente de ,CPU0#CPU1#CPU2 , você pode excluí-lo.

    
por 08.08.2014 / 10:34