Como juntar linhas com colunas únicas a um máximo de 4 colunas em uma linha?

0

Eu tenho um arquivo como abaixo:

1 
4 5 6 7 19
20
22
24 26 27 
29
30
31
32 
34 
40 
50 
56 
58
100
234 235 270 500
1234 1235 1236 1237
2300
2303
2304
2307
2309

Como é claro, existem algumas linhas com mais de uma coluna e outras com apenas uma coluna. Gostaria de unir linhas de coluna única de modo que haja no máximo 4 colunas em cada linha combinada. Então a saída deve ficar assim:

1  
4 5 6 7 19
20 22
24 26 27 
29 30 31 32
34 40 50 56 
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309

Alguma sugestão sobre como isso, considerando que os dados reais são grandes?

    
por zara 31.05.2017 / 22:16

4 respostas

4

Um pouco idiomático, mas trabalhando com o gnu awk:

awk '{printf "%s",(NF==1?$0 FS:(c==0?"":RS) $0 RS)} \
{(NF==1?++c:c=0)} \
c==4{printf "\n";c=0} \
END{printf "\n"}' file

#Output
1 
4 5 6 7 19
20 22 
24 26 27
29 30 31 32 
34 40 50 56 
58 100 
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307 
2309 

Explicação:
variáveis awk:
NF = número de campos
FS = Field Separator = espaço por padrão
RS = Record Separator = nova linha por padrão.
c = contador

Linha1: {printf "%s",(NF==1?$0 FS:(c==0?"":RS) $0 RS)} : ternário aninhado se as operações

#Single ternary if operation:
condition?true action:false action
#Nested if operations:  
condition1?true action 1:(condition2:true action2:false action2) #nested ternary if operations   
-------------------------[            ^ false action1 ^        ]   

Isso pode ser explicado em pseudocódigo como:

if NF==1 then print $0 and print FS   
else (if c==0 then print "" else print RS) and print $0 and print RS again   

Linha 2: {(NF==1?++c:c=0)} : outro ternário se a operação puder ser expressa como:

If NF==1 (line has one field) 
then increase counter c by one 
else reset counter c.  

Linha 3: c==4{printf "\n";c=0} Sintaxe do awk clássico: condition{action}

If counter c==4 then print a new line and reset counter c

Linha 4: END{printf "\n"}' file : isso justs imprime uma nova linha no final do script.

    
por 31.05.2017 / 23:25
2

Você pode usar sed para conseguir o que deseja:

sed -e '
   /./!b
   /[^[:space:]]/!b
   /[^[:space:]][[:blank:]]\{1,\}[^[:space:]]/b

   :loop
      $q;N
      /\n.*\S[[:blank:]]\+\S/b
      s/\n/ /;tdummy
      :dummy
      s/[[:space:]]\{1,\}/&/3;t
   bloop
' yourfile

Explicações

  • Ignore vazio, em branco e linhas com NF > 1.
  • Configure um loop do-while no ponto em que o espaço de padrão contém uma linha de campo único.
  • Pegamos a próxima linha e verificamos se ela tem NF > 1, ponto em que imprimimos todo o espaço do padrão e voltamos a ler a próxima linha.
  • Agora sabemos que a próxima linha também é campo único, então vamos em frente e recortamos a nova linha unindo essas duas partes no espaço padrão.
  • O espaço do padrão tem 3 espaços em pedaços ainda? Se sim, imprimiremos todo o espaço do padrão e começaremos a ler a próxima linha.
  • Caso contrário, voltaremos para o loop que, por sua vez, lerá a próxima linha, mas anexará ao espaço de padrão existente.

Resultado

1
4 5 6 7 19
20 22
24 26 27
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307
2309
    
por 31.05.2017 / 23:42
2

Uso: ./join_rows.awk input.txt

Verifique shebang #!/usr/bin/awk -f , porque a localização awk pode ser diferente no seu sistema.

#!/usr/bin/awk -f

BEGIN {
    count = 1;
}

{
    if (NF == 1) {
        if (count > 1 && count <= 4) printf " ";

        printf "%s", $1;
        count++;

        if (count > 4) {
            printf "\n";
            count = 1;
        }
    } else {
        if (count > 1) printf "\n";

        print;
        count = 1;
    }
}

END {
    if(count > 1) printf "\n";
}

Saída:

1
4 5 6 7 19
20 22 
24 26 27  
29 30 31 32
34 40 50 56
58 100
234 235 270 500
1234 1235 1236 1237 
2300 2303 2304 2307
2309
    
por 01.06.2017 / 13:46
1

Abordagem estendida gawk :

script rearrange_columns.awk :

#!/bin/awk -f
function printRow(a, i, v)
{
    for (i in a) {
        printf "%s ", a[i]
    }
   print ""
   delete a
}
NF <= 2{
    for (i=1; i<=NF; i++) { 
        a[++c] = $i 
        if (length(a) == 4) {
            c = 0 
            printRow(a) 
        }
    }
}
NF > 2{
    if (length(a) > 0) {
        c = 0
        printRow(a)
    }
    print $0 
}
END{ print }

Uso :

awk -f rearrange_columns.awk yourfile

A saída:

1 
4 5 6 7 19
20 22 
24 26 27 
29 30 31 32 
34 40 50 56 
58 100 
234 235 270 500
1234 1235 1236 1237
2300 2303 2304 2307 
2309
    
por 31.05.2017 / 23:46