Como eu mesclo arquivos delimitados por tabulação?

3

Eu tenho 3 arquivos delimitados por tabulação, conforme mostrado abaixo:

Arquivo 1:

1   Hhe.7
2   Hpyl.1
10  Hac.2

Arquivo 2:

3   Hac.2
15  Hpyl.1
33  Hhe.7

Arquivo 3:

70  Hpyl.1
23  Hhe.7
9   Hac.2

Como faço para mesclar esses arquivos (usando a linha de comando) em um arquivo para obter a seguinte saída:

1  33  23  Hhe.7
2  15  70  Hpyl.1
10  3  9  Hac.2
    
por Annemieke Smet 12.04.2016 / 12:55

5 respostas

1

Em um pequeno script python, você pode combinar um número ilimitado de arquivos:

#!/usr/bin/env python3
import sys

#read the files, split the lines for reordering
lines = sum([[l.strip().split() for l in open(f).readlines()]\
             for f in sys.argv[1:]], [])
# get the unique last sections
values = set(map(lambda x:x[1], lines))
# combine them with the combined first sections
newlist = [[y[0] for y in lines if y[1]==x]+[x] for x in values]
for l in newlist:
    print(("\t").join(l))

Copie-o em um arquivo vazio, salve-o como merge.py , execute-o pelo comando:

python3 /path/to/merge.py file1, file2, file3 (file4, file5 etc.)

Saída nos seus arquivos de exemplo:

10  3   9   Hac.2
1   33  23  Hhe.7
2   15  70  Hpyl.1

Adicionando mais arquivos

Como mencionado, o número de arquivos é, em princípio, ilimitado, se eu adicionar um arquivo 4:

40   Hhe.7
50   Hpyl.1
60   Hac.2

e execute o comando:

python3 /path/to/merge.py file1, file2, file3, file4

a saída será:

40  23  33  1   Hhe.7
50  70  15  2   Hpyl.1
60  9   3   10  Hac.2
    
por Jacob Vlijm 12.04.2016 / 13:23
4

A ferramenta clássica do UNIX para isso é join :

NAME
       join - join lines of two files on a common field

SYNOPSIS
       join [OPTION]... FILE1 FILE2

DESCRIPTION
       For  each  pair of input lines with identical join fields, write a line
       to standard output.  The default join field is the first, delimited  by
       blanks.

No entanto, join i) precisa de sua entrada para ser classificada para funcionar e ii) só pode lidar com 2 arquivos. Então, você poderia fazer algo feio e deselegante como:

  1. Classifique cada arquivo no segundo campo e salve como um novo arquivo

    sort -k2 file1 > sorted1
    sort -k2 file2 > sorted2
    sort -k2 file3 > sorted3
    
  2. Junte os arquivos 1 e 2 em um novo arquivo e junte-se ao terceiro

    $ join -j2 --nocheck-order sorted1 sorted2 > newfile
    $ join -o 1.2,1.3,2.1,1.1  -1 1 -2 2 --nocheck-order newfile sorted3 
    10 3 9 Hac.2
    1 33 23 Hhe.7
    2 15 70 Hpyl.1
    

    As opções usadas são:

       -1 FIELD
              join on this FIELD of file 1
    
       -2 FIELD
              join on this FIELD of file 2
       -j FIELD
              equivalent to '-1 FIELD -2 FIELD'
    
       --nocheck-order
              do not check that the input is correctly sorted
    
       -o FORMAT
              obey FORMAT while constructing output line
       FORMAT is one or more  comma  or  blank  separated
       specifications, each being 'FILENUM.FIELD' or '0'. 
    

    Então, esse comando se juntará no primeiro campo do primeiro arquivo e no segundo campo do segundo arquivo, e imprimirá o segundo campo do primeiro arquivo ( 1.2 ), então o terceiro campo do primeiro arquivo ( 1.3 ), o primeiro campo do segundo arquivo ( 2.1 ) e o primeiro campo do primeiro arquivo ( 1.1) .

Alternativamente, você pode combinar a coisa toda em um comando gloriosamente complicado:

$ join -o 1.1,2.2,2.3,2.1 -1 2 -2 1  --nocheck-order <(sort -k2 file3) \
      <(join -j2  --nocheck-order <(sort -k2 file1) <(sort -k2 file2)) 
9 10 3 Hac.2
23 1 33 Hhe.7
70 2 15 Hpyl.1

Se você não gosta de linha de comando arcana, você sempre pode usar um pequeno script:

$ awk '{a[$NF]="\t"a[$NF];} END{for(i in a){print a[i],i}}' file{1,2,3} 
23  33  1    Hhe.7
9   3   10   Hac.2
70  15  2    Hpyl.1
    
por terdon 12.04.2016 / 13:28
3

Este é um trabalho para join , que pode se unir em campos comuns de dois arquivos:

$ join -11 -22 -o1.2,1.3,2.1,0 <(join -j2 <(sort -k2,2 f1.txt) <(sort -k2,2 f2.txt)) <(sort -k2,2 f3.txt)
10 3 9 Hac.2
1 33 23 Hhe.7
2 15 70 Hpyl.1

Como join leva apenas dois arquivos de entrada de cada vez, usamos a substituição de processo ( <() ) para passar a saída de join -ing primeiros dois arquivos com o terceiro.

    
por heemayl 12.04.2016 / 13:30
1

com awk :

awk -F"\t" -v OFS="\t" '!(a[]){a[]=;next}
{a[]=a[]"\t"} 
END{
for ( i in a) {
    print a[i],i
    }
}'
    
por 7171u 12.04.2016 / 13:31
0

Resposta de:

  

O seguinte script deve fazer uma junção externa na coluna (campo) 1 de   todos os arquivos delimitados por tabulações foram passados como argumentos. Ele usa o    inscreva-se   comando, que faz uma junção externa em arquivos classificados, 2 arquivos de cada vez.

     

Ele se juntará a todas as linhas dos arquivos, incluindo as linhas de cabeçalho. E se   você deseja que os cabeçalhos sejam excluídos, altere os dois comandos sort para   algo que produz um arquivo ordenado que os omite.

#!/bin/sh
if test $# -lt 2
then
    echo usage: gjoin file1 file2 ...
    exit 1
fi
sort -t $'\t' -k 1 "" > result
shift
for f in "$@"
do
    sort -t $'\t' -k 1 "$f" > temp
    join -1 1 -2 1 -t $'\t' result temp > newresult
    mv newresult result
done
cat result
rm result temp
     

Se você tiver um shell mais antigo, $'\t' não será substituído por uma guia.   você precisará usar ' TAB ' , onde você   coloque uma tabulação literal entre as aspas.

     

Otimizações são possíveis se, em vez de /bin/sh , você puder usar um   shell moderno como bash ou ksh; por exemplo, as linhas

sort -t $'\t' -k 1 "$f" > temp
join -1 1 -2 1 -t $'\t' result temp > newresult
     

pode ser substituído por

join -1 1 -2 1 -t $'\t' result <(sort -t $'\t' -k 1 "$f") > newresult
    
por blade19899 12.04.2016 / 13:20