UNIX cola colunas e insere zeros para todos os valores ausentes

UNIX cola colunas e insere zeros para todos os valores ausentes

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 (conforme 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 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 de inserir zeros em vez de omitir a linha em si?

Preciso combinar 100 arquivos para que meu arquivo de saída contenha 102 colunas.

Responder1

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

mesclar.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]
  }
}

Aqui estão alguns arquivos de teste : f1, e :f2f3f4

$ 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 precisar ser separada por tabulações, defina o separador do campo de saída de acordo:

awk -f merge.awk OFS='\t' f[1-4]

Responder2

Experimente 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

Responder3

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 ideia é imprimir todos os arquivos e depois capturar os valores repetidos no array a[$1 $2]. Além disso, b[$1 $2]contém a quantidade de vezes que um par ( $1, $2) apareceu.

No END{}bloco, continuamos percorrendo os valores e completando com tantos 0s quanto faltam do número de elementos até o número máximo de elementos.

informação relacionada