UNIX cola colunas e insere zeros para todos os valores ausentes

5

Eu gostaria de mesclar colunas específicas de dois arquivos txt contendo um número variável de linhas, mas o mesmo número de colunas (como mostrado abaixo):

  file1:
  xyz   desc1   12
  uvw   desc2   55
  pqr   desc3   12

  file2:
  xyz   desc1   56
  uvw   desc2   88


  Preferred output:
  xyz   desc1   12  56
  uvw   desc2   55  88
  pqr   desc3   12   0

Atualmente eu uso o comando paste usando o awk como:

  paste <(awk '{print $1}' file1) <(awk '{print $2}' file1) <(awk '{print $3}' file1) <(awk '{print $3}' file2) 

Mas isso parece mesclar apenas colunas que se sobrepõem. Existe uma maneira no awk para inserir zeros em vez de omitir a própria linha?

Eu preciso combinar 100 arquivos juntos para que meu arquivo de saída contenha 102 colunas.

    
por biobudhan 12.06.2014 / 14:43

3 respostas

3

Se a ordem das colunas for importante, ou seja, os números do mesmo arquivo devem ser mantidos na mesma coluna, você precisa adicionar preenchimento ao ler os arquivos diferentes. Aqui está uma maneira que funciona com o GNU awk:

merge.awk

# Set k to be a shorthand for the key
{ k = $1 SUBSEP $2 }

# First element with this key, add zeros to align it with other rows
!(k in h) {
  for(i=1; i<=ARGIND-1; i++)
    h[k] = h[k] OFS 0 
}

# Remember the data element
{ h[k] = h[k] OFS $3 }

# Before moving to the next file, ensure that all rows are aligned
ENDFILE {
  for(k in h) {
    if(split(h[k], a) < ARGIND)
      h[k] = h[k] OFS 0
  }
}

# Print out the collected data
END {
  for(k in h) {
    split(k, a, SUBSEP)
    print a[1], a[2], h[k]
  }
}

Veja alguns arquivos de teste: f1 , f2 , f3 e f4 :

$ tail -n+1 f[1-4]
==> f1 <==
xyz desc1 21
uvw desc2 22
pqr desc3 23

==> f2 <==
xyz desc1 56
uvw desc2 57

==> f3 <==
xyz desc1 87
uvw desc2 88

==> f4 <==
xyz desc1 11
uvw desc2 12
pqr desc3 13
stw desc1 14
arg desc2 15

Teste 1

awk -f merge.awk f[1-4] | column -t

Saída:

pqr  desc3  23  0   0   13
uvw  desc2  22  57  88  12
stw  desc1  0   0   0   14
arg  desc2  0   0   0   15
xyz  desc1  21  56  87  11

Teste 2

awk -f merge.awk f2 f3 f4 f1 | column -t

Saída:

pqr  desc3  0   0   13  23
uvw  desc2  57  88  12  22
stw  desc1  0   0   14  0
arg  desc2  0   0   15  0
xyz  desc1  56  87  11  21

Editar:

Se a saída deve ser separada por tabulações, defina o separador do campo de saída de acordo:

awk -f merge.awk OFS='\t' f[1-4]
    
por 12.06.2014 / 20:14
2

Tente isto:

$ awk '
    FNR == NR { a[$1,$2] = $3; next }
    {
        print $0,(($1,$2) in a) ? a[$1,$2] : "0"
    }
' file2 file1
xyz   desc1   12 56
uvw   desc2   55 88
pqr   desc3   12 0
    
por 12.06.2014 / 14:53
2

Isso é um pouco longo, mas funciona:

$ cat file1 file2 | awk '{a[$1FS$2]=a[$1FS$2]FS$3; b[$1FS$2]++} END {for (i in b) max=max<b[i]?b[i]:max; for (i in a) {printf "%s %s", i, a[i]; for (j=b[i]; j<max; j++) printf "%s0", FS  printf "%s", RS}}' 
pqr desc3  12 0
xyz desc1  12 56
uvw desc2  55 88

O bloco awk pode ser formatado assim:

awk '{a[$1FS$2]=a[$1FS$2]FS$3; b[$1FS$2]++}
      END {for (i in b) max=max<b[i]?b[i]:max
          for (i in a) {printf "%s%s%s", i, FS, a[i]
                        for (j=b[i]; j<max; j++) printf "%s0", FS
                        printf "%s", RS}
          }'

A idéia é imprimir todos os arquivos e, em seguida, capturar os valores repetidos na matriz a[$1 $2] . Além disso, b[$1 $2] contém a quantidade de vezes que um par ( $1 , $2 ) apareceu.

No bloco END{} , continuamos fazendo o loop através dos valores e completando com o mesmo 0 s do número de elementos para o número máximo de elementos.

    
por 12.06.2014 / 15:02