Como unir linhas com colunas únicas até no máximo 4 colunas em uma linha?

Como unir linhas com colunas únicas até no máximo 4 colunas em uma linha?

Eu tenho um arquivo parecido com o 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 está claro, existem algumas linhas com mais de 1 coluna e outras com apenas uma coluna. Gostaria de unir linhas de coluna única de forma que houvesse 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 de como fazer isso, considerando que os dados reais são grandes?

Responder1

Um pouco idiomático, mas trabalhando com 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=Separador de Campos = espaço por padrão
RS=Separador de Registros= nova linha por padrão.
c = contador

Linha 1: {printf "%s",(NF==1?$0 FS:(c==0?"":RS) $0 RS)}: operações if ternárias aninhadas

#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)}: Outra operação if ternária que pode 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 clássica do awk:condition{action}

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

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

Responder2

Você pode usar sedpara 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

  • Pule linhas vazias, em branco e com NF > 1.
  • Configure um loop do-while no ponto onde o espaço padrão contém uma linha de campo único.
  • Pegamos a próxima linha e verificamos se ela possui NF > 1, momento em que imprimimos todo o espaço do padrão e voltamos à leitura da próxima linha.
  • Agora sabemos que a próxima linha também é de campo único, então seguimos em frente e cortamos a nova linha que une essas duas partes no espaço padrão.
  • O espaço padrão já tem 3 pedaços de espaços? Se sim, imprimimos todo o espaço do padrão e começamos a ler a próxima linha.
  • Caso contrário, voltamos ao loop que, por sua vez, lerá a próxima linha, mas a anexará ao espaço 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

Responder3

Uso: ./join_rows.awk input.txt

Verifique shebang #!/usr/bin/awk -f, porque a awklocalização 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

Responder4

Estendidoficar boquiabertoabordagem:

rearranjar_colunas.awkroteiro:

#!/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

informação relacionada