web-dev-qa-db-fra.com

Combinez deux fichiers avec awk

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Sortie recherchée:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Comment puis-je le faire?

9
pawana

La réponse ci-dessous est basée sur un Q & A similaire à SO avec quelques modifications pertinentes:

$ 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

L'idée est de créer une carte de hachage avec index et de l'utiliser comme dictionnaire.

Pour la deuxième question que vous avez posée dans votre commentaire (), que devrait-on changer si la deuxième colonne de file1.txt sera la sixième colonne ):

Si le fichier d'entrée ressemble à 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

La commande suivante le fera:

$ 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    
11
Yaron

Je sais que vous avez dit awk, mais il existe une commande join à cette fin ...

{
  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

Cela serait suffisant avec la première commande join s'il n'y avait pas cette ligne:

item4   platD

La commande dit essentiellement: join en fonction de la deuxième colonne du premier fichier (-1 2) et de la première colonne du deuxième fichier (-2 1), et affiche la première colonne du premier fichier et la deuxième colonne du deuxième fichier (-o 1.1,2.2 ). Cela ne montre que les lignes qui sont liées. La deuxième commande de jointure dit à peu près la même chose, mais elle dit de montrer les lignes du premier fichier qui ne pouvaient pas être appariées (-v 1) et d'afficher la première colonne du premier fichier et la deuxième colonne du premier fichier (-o 1.1,1.2 ). Ensuite, nous trions la sortie des deux combinés. sort -k 1 signifie un tri basé sur la première colonne, et sort -k 2 signifie un tri basé sur la seconde. Il est important de trier les fichiers en fonction de la colonne join avant de les transmettre à join.

Maintenant, j’ai écrit le tri à deux reprises, parce que je n’aime pas encombrer mes répertoires de fichiers si je peux l’aider. Toutefois, comme l’a dit David Foerster, selon la taille des fichiers, vous pouvez trier les fichiers et les enregistrer d’abord pour ne pas avoir à attendre pour les trier deux fois. Pour donner une idée des tailles, voici le temps qu'il faut pour trier 1 million et 10 millions de lignes sur mon ordinateur:

$ 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

Cela représente 1,5 seconde pour 1 million de lignes et 19 secondes pour 10 millions de lignes.

6
JoL