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 eu posso fazer isso?

pawana
fonte

Respostas:

11

A resposta abaixo é baseada em perguntas e respostas semelhantes no 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 idéia é criar um mapa de hash com índice e usá-lo como dicionário.

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

Se o arquivo de entrada for como 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    
Yaron
fonte
11
@pawana - Atualizei minha resposta para também resolver sua segunda pergunta no comentário. Se eu respondi sua pergunta, aceite -a.
Yaron
6

Eu sei que você disse awk, mas há um joincomando para esse fim ...

{
  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 joincomando se não fosse para esta linha:

item4   platD

O comando basicamente diz: junção com base na segunda coluna do primeiro arquivo ( -1 2) e na primeira coluna do segundo arquivo ( -2 1) e gera a primeira coluna do primeiro arquivo e a segunda coluna do segundo arquivo ( -o 1.1,2.2). Isso mostra apenas as linhas que emparelhadas. O segundo comando join diz quase a mesma coisa, mas diz para mostrar as linhas do primeiro arquivo que não puderam ser emparelhadas ( -v 1) e gerar a primeira coluna do primeiro arquivo e a segunda coluna do primeiro arquivo ( -o 1.1,1.2). Depois, classificamos a saída de ambos combinados. sort -k 1significa classificar com base na primeira coluna e sort -k 2significa classificar com base na segunda. É importante classificar os arquivos com base na coluna de junção antes de passá-los para join.

Agora, escrevi a classificação duas vezes, porque não gosto de desarrumar meus diretórios com arquivos, se eu puder ajudar. No entanto, como David Foerster disse, dependendo do tamanho dos arquivos, convém classificá-los e salvá-los primeiro para não ter que esperar para classificar cada um duas vezes. Para dar uma idéia dos tamanhos, eis 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.

JoL
fonte
Nesse caso, seria melhor armazenar os dados de entrada classificados em arquivos intermediários (temporários), porque a classificação demora muito para conjuntos de dados de tamanho não trivial. Caso contrário, +1.
David Foerster
@ David É um bom ponto. Pessoalmente, eu realmente não gosto de criar arquivos intermediários, mas também estou impaciente com processos de execução longa. Eu me perguntava qual seria o "tamanho trivial" e, portanto, fiz uma pequena referência e a adicionei à resposta, juntamente com a sua sugestão.
JOL
Classificar 1 milhão de registros é rápido o suficiente em computadores desktop razoavelmente modernos. Com mais 2 ordens de grandeza, mais coisas começam a se tornar interessantes. De qualquer forma, o tempo decorrido (real) ( %Eno formato de hora) é menos interessante para medir o desempenho computacional. O tempo de CPU do modo de usuário ( %Uou simplesmente uma TIMEFORMATvariável não configurada ) seria muito mais significativo.
David Foerster
@ David Eu não estou realmente familiarizado com os casos de uso para os diferentes momentos. Por que isso é mais interessante? Tempo decorrido é o que coincide com o tempo que realmente estou esperando. Para o comando de 1,5 segundo, estou recebendo 4,5 segundos com %U.
JOL
11
O tempo decorrido é afetado pelo tempo gasto na espera de outras tarefas em execução no mesmo sistema e no bloqueio de solicitações de E / S. (Usuário) O tempo da CPU não é. Geralmente, ao comparar a velocidade dos algoritmos computacionalmente vinculados, deseja-se desconsiderar a E / S e evitar erros de medição devido a outras tarefas em segundo plano. A questão importante é "Quanto de computação esse algoritmo requer nesse conjunto de dados?" em vez de "Quanto tempo meu computador gastou em todas as suas tarefas enquanto aguardava a conclusão desse cálculo?"
David Foerster