
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 :f2
f3
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 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 0
s quanto faltam do número de elementos até o número máximo de elementos.