Combine dois arquivos com o awk

9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Saída desejada:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Como posso fazer isso?

    
por pawana 20.03.2018 / 13:33

2 respostas

11

A resposta abaixo é baseada em um Perguntas e Respostas semelhantes em SO com algumas modificações relevantes:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($2 in dict) ? dict[$2] : $2}1' file2.txt file1.txt 
item1 platA
item2 platB
item3 platC
item4 platD
item5 platE

A ideia é criar um mapa hash com índice e usá-lo como dicionário.

Para a segunda pergunta que você fez em seu comentário ( o que deve ser alterado se a segunda coluna de file1.txt for a sexta coluna ):

Se o arquivo de entrada for igual a file1b.txt :

item1 A5 B C D carA
item2 A4 1 2 3 carB
item3 A3 2 3 4 carC
item4 A2 4 5 6 platD
item5 A1 7 8 9 carE

O seguinte comando fará isso:

$ awk 'FNR==NR {dict[$1]=$2; next} {$2=($6 in dict) ? dict[$6] : $6;$3="";$4="";$5="";$6=""}1' file2.txt file1b.txt 
item1 platA    
item2 platB    
item3 platC    
item4 platD    
item5 platE    
    
por Yaron 20.03.2018 / 13:38
6

Eu sei que você disse awk , mas existe um comando join para essa finalidade ...

{
  join -o 1.1,2.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt)     
  join -v 1 -o 1.1,1.2 -1 2 -2 1 <(sort -k 2 File1.txt) <(sort -k 1 File2.txt) 
} | sort -k 1

Seria suficiente com o primeiro comando join se não fosse por esta linha:

item4   platD

O comando basicamente diz: join baseado na segunda coluna do primeiro arquivo ( -1 2 ), e a primeira coluna do segundo arquivo ( -2 1 ), e produz a primeira coluna do primeiro arquivo e a segunda coluna do segundo arquivo ( -o 1.1,2.2 ). Isso mostra apenas as linhas que foram emparelhadas. O segundo comando join diz quase a mesma coisa, mas diz para mostrar as linhas do primeiro arquivo que não pôde ser emparelhado ( -v 1 ), e mostra a primeira coluna do primeiro arquivo e a segunda coluna do primeiro arquivo (%código%). Então nós classificamos a saída de ambos combinados. -o 1.1,1.2 significa classificar com base na primeira coluna e sort -k 1 significa classificar com base no segundo. É importante classificar os arquivos com base na coluna de associação antes de passá-los para sort -k 2 .

Agora, escrevi a classificação duas vezes, porque não gosto de arrumar meus diretórios com arquivos, se puder ajudar. No entanto, como David Foerster disse, dependendo do tamanho dos arquivos, você pode querer ordenar os arquivos e salvá-los primeiro para não ter espera para ordenar cada um duas vezes. Para ter uma ideia dos tamanhos, aqui está o tempo necessário para classificar 1 milhão e 10 milhões de linhas no meu computador:

$ ruby -e '(1..1000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 1million.txt 
$ ruby -e '(1..10000000).each {|i| puts "item#{i}   plat#{i}"}' | shuf > 10million.txt 
$ head 10million.txt 
item530284   plat530284
item7946579   plat7946579
item1521735   plat1521735
item9762844   plat9762844
item2289811   plat2289811
item6878181   plat6878181
item7957075   plat7957075
item2527811   plat2527811
item5940907   plat5940907
item3289494   plat3289494
$ TIMEFORMAT=%E
$ time sort 1million.txt >/dev/null
1.547
$ time sort 10million.txt >/dev/null
19.187

São 1,5 segundos para 1 milhão de linhas e 19 segundos para 10 milhões de linhas.

    
por JoL 20.03.2018 / 17:11