Combinez deux fichiers avec awk

9

File1.txt

item1   carA
item2   carB
item3   carC
item4   platD
item5   carE

File2.txt

carA  platA
carB  platB
carC  platC
carE  platE

Sortie souhaitée:

item1   platA
item2   platB
item3   platC
item4   platD
item5   platE

Comment puis-je le faire?

pawana
la source

Réponses:

11

La réponse ci-dessous est basée sur un Q & A similaire dans 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 2e question que vous avez posée dans votre commentaire ( que faut-il changer si la deuxième colonne de file1.txtsera la sixième colonne ):

Si le fichier d'entrée sera comme 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    
Yaron
la source
1
@pawana - J'ai mis à jour ma réponse pour résoudre également votre deuxième question en commentaire. Si j'ai répondu à votre question, veuillez l' accepter .
Yaron
6

Je sais que tu as dit awk, mais il y a une joincommande à cet effet ...

{
  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

Ce serait suffisant avec la première joincommande s'il n'y avait pas cette ligne:

item4   platD

La commande dit essentiellement: jointure basée sur la deuxième colonne du premier fichier ( -1 2) et 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 appariées. La deuxième commande join indique presque la même chose, mais elle indique d'afficher les lignes du premier fichier qui n'ont pas pu être jumelées ( -v 1), et de sortir 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 1signifie trier en fonction de la première colonne et sort -k 2signifie trier en fonction de la seconde. Il est important de trier les fichiers en fonction de la colonne de jointure avant de les transmettre join.

Maintenant, j'ai écrit le tri deux fois, parce que je n'aime pas joncher mes répertoires de fichiers si je peux l'aider. Cependant, comme l'a dit David Foerster, selon la taille des fichiers, vous souhaiterez peut-être trier les fichiers et les enregistrer d'abord pour ne pas avoir à attendre de 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.

JoL
la source
Dans ce cas, il serait préférable de stocker les données d'entrée triées dans des fichiers intermédiaires (temporaires) car le tri prend assez de temps pour les ensembles de données de taille non triviale. Sinon +1.
David Foerster
@David C'est un bon point. Personnellement, je n'aime vraiment pas avoir à créer des fichiers intermédiaires, mais je suis également impatient avec les processus longs. Je me demandais ce que serait "de taille triviale", alors j'ai fait un petit point de repère et l'ai ajouté à la réponse avec votre suggestion.
JoL
Trier 1 million d'enregistrements est assez rapide sur des ordinateurs de bureau raisonnablement modernes. Avec 2 autres 3 ordres de grandeur, plus de choses commencent à devenir intéressantes. Dans tous les cas, le temps (réel) écoulé (le %Eformat horaire) est moins intéressant pour mesurer les performances de calcul. Le temps CPU en mode utilisateur ( %Uou simplement une TIMEFORMATvariable non définie ) serait beaucoup plus significatif.
David Foerster
@David Je ne connais pas vraiment les cas d'utilisation pour les différentes époques. Pourquoi est-ce plus intéressant? Le temps écoulé est ce qui coïncide avec le temps que j'attends réellement. Pour la commande de 1,5 seconde, je reçois 4,5 secondes avec %U.
JoL
1
Le temps écoulé est affecté par le temps passé à attendre d'autres tâches s'exécutant sur le même système et à bloquer les demandes d'E / S. (Utilisateur) Le temps CPU n'est pas. Habituellement, lorsque l'on compare la vitesse des algorithmes liés au calcul, on veut ignorer les E / S et éviter les erreurs de mesure dues à d'autres tâches d'arrière-plan. La question importante est "Combien de calculs cet algorithme requiert-il sur cet ensemble de données?" au lieu de "Combien de temps mon ordinateur a-t-il consacré à toutes ses tâches en attendant la fin de ce calcul?"
David Foerster