En travaillant sous Linux/Shell env, comment puis-je accomplir les tâches suivantes:
le fichier texte 1 contient:
1
2
3
4
5
le fichier texte 2 contient:
6
7
1
2
3
4
J'ai besoin d'extraire les entrées du fichier 2 qui ne sont pas dans le fichier 1. Donc, "6" et "7" dans cet exemple.
Comment faire cela depuis la ligne de commande?
merci beaucoup!
$ awk 'FNR==NR {a[$0]++; next} !a[$0]' file1 file2
6
7
Explication du fonctionnement du code:
Explication des détails:
FNR
est le numéro d'enregistrement du fichier en coursNR
est le numéro d'enregistrement global actuel de tous les fichiers d'entréeFNR==NR
n'est vrai que lorsque nous lisons le fichier1$0
est la ligne de texte actuellea[$0]
est un hachage avec la clé définie sur la ligne de texte actuellea[$0]++
suit que nous avons vu la ligne de texte actuelle!a[$0]
n'est vrai que lorsque nous n'avons pas vu le texte de la ligneUtilisation de certains utilitaires moins connus:
sort file1 > file1.sorted
sort file2 > file2.sorted
comm -1 -3 file1.sorted file2.sorted
Cela produira des doublons, donc s'il y a 1 3
dans file1
, mais 2 dans file2
, cela affichera toujours 1 3
. Si ce n'est pas ce que vous voulez, dirigez la sortie de sort
à uniq
avant de l'écrire dans un fichier:
sort file1 | uniq > file1.sorted
sort file2 | uniq > file2.sorted
comm -1 -3 file1.sorted file2.sorted
Il y a beaucoup d'utilitaires dans le paquet coreutils GNU qui permettent toutes sortes de manipulations de texte.
Je me demandais laquelle des solutions suivantes était la plus "rapide" pour les fichiers "plus gros":
awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2 # awk1 by SiegeX
awk 'FNR==NR{a[$0]++;next}!($0 in a)' file1 file2 # awk2 by ghostdog74
comm -13 <(sort file1) <(sort file2)
join -v 2 <(sort file1) <(sort file2)
grep -v -F -x -f file1 file2
Résultats de mes benchmarks en bref:
grep -Fxf
, c'est beaucoup plus lent (2 à 4 fois dans mes tests).comm
est légèrement plus rapide que join
.comm
et join
sont beaucoup plus rapides que awk1 + awk2. (Bien sûr, ils n'assument pas de fichiers triés.)comm
probablement en raison du fait qu'il utilise plus de threads. Les temps de CPU sont inférieurs pour awk1 + awk2.Par souci de concision, j'omets tous les détails. Cependant, je suppose que toute personne intéressée peut me contacter ou simplement répéter les tests. En gros, la configuration était
# Debian Squeeze, Bash 4.1.5, LC_ALL=C, slow 4 core CPU
$ wc file1 file2
321599 321599 8098710 file1
321603 321603 8098794 file2
Résultats typiques des courses les plus rapides
awk2: real 0m1.145s user 0m1.088s sys 0m0.056s user+sys 1.144
awk1: real 0m1.369s user 0m1.324s sys 0m0.044s user+sys 1.368
comm: real 0m0.980s user 0m1.608s sys 0m0.184s user+sys 1.792
join: real 0m1.080s user 0m1.756s sys 0m0.140s user+sys 1.896
grep: real 0m4.005s user 0m3.844s sys 0m0.160s user+sys 4.004
BTW, pour les awkies: Il semble que a[$0]=1
est plus rapide que a[$0]++
, et (!($0 in a))
est plus rapide que (!a[$0])
. Donc, pour une solution awk, je suggère:
awk 'FNR==NR{a[$0]=1;next}!($0 in a)' file1 file2
avec grep:
grep -F -x -v -f file_1 file_2
Que diriez-vous:
diff file_1 file_2 | grep '^>' | cut -c 3-
Cela imprimerait les entrées du fichier_2 qui ne se trouvent pas dans le fichier_1. Pour le résultat inverse, il suffit de remplacer ">" par "<". 'cut' supprime les deux premiers caractères ajoutés par 'diff', qui ne font pas partie du contenu original.
Les fichiers n'ont même pas besoin d'être triés.
voici une autre solution awk
$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
6
7
Si vous êtes vraiment déterminé à le faire à partir de la ligne de commande, ce site (recherchez "aucun doublon trouvé") a un exemple awk
qui recherche les doublons. C'est peut-être un bon point de départ pour examiner cela.
Cependant, je vous encourage à utiliser Perl ou Python pour cela. Fondamentalement, le flux du programme serait:
findUniqueValues(file1, file2){
contents1 = array of values from file1
contents2 = array of values from file2
foreach(value2 in contents2){
found=false
foreach(value1 in contents1){
if (value2 == value1) found=true
}
if(!found) print value2
}
}
Ce n'est pas la façon la plus élégante de le faire, car elle a une complexité temporelle O (n ^ 2), mais elle fera l'affaire.