web-dev-qa-db-fra.com

Commande Unix pour trouver des lignes communes dans deux fichiers

Je suis sûr que j’ai déjà trouvé une commande unix capable d’imprimer les lignes communes à partir de deux fichiers ou plus. Quelqu'un connaît-il son nom? C'était beaucoup plus simple que diff.

167
too much php

La commande que vous recherchez est comm . par exemple:-

comm -12 1.sorted.txt 2.sorted.txt

Ici:

- 1 : supprime la colonne 1 (lignes uniques à 1.sorted.txt)

- 2 : supprime la colonne 2 (lignes uniques à 2.sorted.txt)

200
Jonathan Leffler

Pour appliquer facilement la commande comm aux fichiers non triés , utilisez les commandes de Bash - substitution de processus :

$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321

Les fichiers abc et def ont donc une ligne commune, celle avec "132". Utilisation de comm sur des fichiers non triés:

$ comm abc def
123
    132
567
132
    777
    321
$ comm -12 abc def # No output! The common line is not found
$

La dernière ligne n'a généré aucune sortie, la ligne commune n'a pas été découverte.

Maintenant, utilisez comm sur les fichiers triés, en triant les fichiers avec substitution de processus:

$ comm <( sort abc ) <( sort def )
123
            132
    321
567
    777
$ comm -12 <( sort abc ) <( sort def )
132

Maintenant, nous avons la ligne 132!

58
Stephan Wehner

Pour compléter le Perl one-liner, voici son équivalent awk:

awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

Cela lira toutes les lignes de file1 dans le tableau arr[], puis vérifiez chaque ligne de file2 s'il existe déjà dans le tableau (c'est-à-dire file1). Les lignes trouvées seront imprimées dans l'ordre dans lequel elles apparaissent dans file2. Notez que la comparaison in arr utilise la ligne entière de file2 en tant qu'index du tableau, il ne signalera que les correspondances exactes sur des lignes entières.

25
Tatjana Heuser

Peut-être que vous voulez dire comm?

Comparez les fichiers triés FILE1 et FILE2 ligne par ligne.

En l'absence d'options, créez une sortie sur trois colonnes. La première colonne contient des lignes uniques à FILE1, la deuxième colonne contient des lignes uniques à FILE2 et la troisième colonne contient des lignes communes aux deux fichiers.

Le secret pour trouver ces informations sont les pages d’informations. Pour les programmes GNU, ils sont beaucoup plus détaillés que leurs pages de manuel. Essayez info coreutils et il vous listera tous les petits utilitaires utiles.

24

Tandis que

grep -v -f 1.txt 2.txt > 3.txt

vous donne les différences de deux fichiers (ce qui est dans 2.txt et non dans 1.txt), vous pouvez facilement faire un

grep -f 1.txt 2.txt > 3.txt

pour collecter toutes les lignes communes, ce qui devrait fournir une solution simple à votre problème. Si vous avez trié les fichiers, vous devriez néanmoins prendre comm. Cordialement!

18
ferdy

Si les deux fichiers ne sont pas encore triés, vous pouvez utiliser:

comm -12 <(sort a.txt) <(sort b.txt)

et cela fonctionnera en évitant le message d'erreur comm: file 2 is not in sorted order en faisant comm -12 a.txt b.txt.

8
Basj
Perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2
8
user2592005
awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2
5
R S John

Sur une version limitée de Linux (comme un QNAP (nas) sur lequel je travaillais):

  • la communication n'existait pas
  • grep -f file1 file2 peut causer des problèmes, comme l’a dit @ChristopherSchultz, en utilisant grep -F -f file1 file2 était vraiment lent (plus de 5 minutes - pas terminé - plus de 2-3 secondes avec la méthode ci-dessous pour les fichiers de plus de 20 Mo)

Alors voici ce que j'ai fait:

sort file1 > file1.sorted
sort file2 > file2.sorted

diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted

Si files.same.sorted doit avoir été dans le même ordre que celui d'origine, puis ajouter cette ligne pour le même ordre que fichier1:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

ou, pour le même ordre que file2:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same
3
Master DJon

Juste pour référence si quelqu'un cherche toujours comment faire cela pour plusieurs fichiers, voir la réponse liée à Recherche des lignes correspondantes sur plusieurs fichiers.


En combinant ces deux réponses ( ans1 et ans2 ), je pense que vous pouvez obtenir le résultat souhaité sans trier les fichiers:

#!/bin/bash
ans="matching_lines"

for file1 in *
do 
    for file2 in *
        do 
            if  [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
                echo "Comparing: $file1 $file2 ..." >> $ans
                Perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done 
done

Enregistrez-le simplement, donnez-lui les droits d'exécution (chmod +x compareFiles.sh) et lancez-le. Il prendra tous les fichiers présents dans le répertoire de travail en cours et fera une comparaison générale en laissant dans le fichier "matching_lines" le résultat.

Les choses à améliorer:

  • Ignorer les répertoires
  • Évitez de comparer tous les fichiers deux fois (fichier1 contre fichier2 et fichier2 contre fichier1).
  • Peut-être ajouter le numéro de ligne à côté de la chaîne correspondante
2
akarpovsky