Comparaison de deux fichiers dans le terminal Linux


168

Il existe deux fichiers appelés "a.txt" et "b.txt" qui ont tous deux une liste de mots. Maintenant, je veux vérifier quels mots sont supplémentaires dans "a.txt" et ne sont pas dans "b.txt" .

J'ai besoin d'un algorithme efficace car j'ai besoin de comparer deux dictionnaires.


27
diff a.txt b.txtn'est pas assez?
ThanksForAllTheFish

Les mots peuvent-ils apparaître plusieurs fois dans chaque fichier? Pouvez-vous trier les fichiers?
Basile Starynkevitch

je n'ai besoin que des mots qui ne sont pas présents dans "b.txt" et qui sont présents dans a.txt
Ali Imran

Réponses:


343

si vous avez installé vim, essayez ceci:

vimdiff file1 file2

ou

vim -d file1 file2

vous le trouverez fantastique.entrez la description de l'image ici


9
vraiment génial, bon dans la conception et facile à découvrir les différences. Ohmygod
Zen

1
Votre réponse est géniale, mais mon professeur m'a demandé de n'utiliser aucune fonction de bibliothèque: P
Ali Imran

1
Quel bel outil! Ceci est extrêmement utile.
user1205577

1
Quelle est la signification de ces couleurs?
zygimantus

1
Les codes colorés signifient qu'ils sont différents dans deux fichiers. @zygimantus
Fengya Li

73

Triez-les et utilisez comm:

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

commcompare les fichiers d'entrée (triés) et génère par défaut trois colonnes: les lignes uniques à a, les lignes uniques à b et les lignes présentes dans les deux. En spécifiant -1, -2et / ou -3vous pouvez supprimer la sortie correspondante. Par conséquent comm -23 a b, ne répertorie que les entrées uniques à un fichier. J'utilise la <(...)syntaxe pour trier les fichiers à la volée, s'ils sont déjà triés, vous n'en avez pas besoin.


J'ai ajouté ma propre réponse en utilisant uniquement les commandes grep, veuillez me dire est-ce plus efficace?
Ali Imran

3
@AliImran, commest plus efficace car il effectue le travail en une seule exécution, sans stocker l'intégralité du fichier en mémoire. Puisque vous utilisez des dictionnaires qui sont probablement déjà triés, vous n'en avez même pas besoin sort. L'utilisation grep -f file1 file2d'un autre côté chargera le tout file1en mémoire et comparera chaque ligne file2avec toutes ces entrées, ce qui est beaucoup moins efficace. C'est surtout utile pour les petits, non triés -f file1.
Anders Johansson

1
Merci @AndersJohansson pour le partage de la commande "comm". C'est vraiment chouette. Je dois souvent faire des jointures externes entre des fichiers et cela fait l'affaire.
blispr le

Faites attention au caractère de nouvelle ligne ... Je viens de trouver qui \nsera également inclus pour faire la comparaison.
Bin


28

Vous pouvez utiliser l' diffoutil sous Linux pour comparer deux fichiers. Vous pouvez utiliser les options --changed-group-format et --unchanged-group-format pour filtrer les données requises.

Les trois options suivantes peuvent être utilisées pour sélectionner le groupe approprié pour chaque option:

  • '% <' récupère les lignes de FILE1

  • '%>' récupère les lignes de FILE2

  • '' (chaîne vide) pour supprimer des lignes des deux fichiers.

Par exemple: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

Si vous préférez le style de sortie diff git diff, vous pouvez l'utiliser avec l' --no-indexindicateur pour comparer des fichiers qui ne se trouvent pas dans un référentiel git:

git diff --no-index a.txt b.txt

En utilisant quelques fichiers avec environ 200k chaînes de noms de fichiers chacun, j'ai comparé (avec la timecommande intégrée ) cette approche par rapport à certaines des autres réponses ici:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commsemble être de loin la plus rapide, alors qu'elle git diff --no-indexsemble être l'approche la plus rapide pour la sortie de style diff.


Mise à jour 25/03/2018 Vous pouvez en fait omettre l' --no-indexindicateur sauf si vous êtes dans un référentiel git et que vous souhaitez comparer les fichiers non suivis dans ce référentiel. Depuis les pages de manuel :

Ce formulaire permet de comparer les deux chemins donnés sur le système de fichiers. Vous pouvez omettre l'option --no-index lors de l'exécution de la commande dans un arbre de travail contrôlé par Git et au moins l'un des chemins pointe en dehors de l'arbre de travail, ou lors de l'exécution de la commande en dehors d'un arbre de travail contrôlé par Git.




4

Utilisation comm -13 (nécessite des fichiers triés) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

Voici ma solution pour cela:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
Avez-vous essayé l'une des autres solutions? L'une de ces solutions vous a-t-elle été utile? Votre question est suffisamment générique pour attirer de nombreux utilisateurs, mais votre réponse est plus spécifique à mon goût ... Pour mon cas particulier, cela a sdiff -s file1 file2été utile.
Metafaniel

@Metafaniel ma solution n'utilise pas la commande sdiff. Il n'utilise que les commandes intégrées de Linux pour résoudre le problème.
Ali Imran

-1

Utiliser awk pour cela. Fichiers de test:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

Le awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Les doublons sont générés:

four
four

Pour éviter les doublons, ajoutez chaque mot nouvellement rencontré dans a.txt au seenhachage:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Production:

four

Si les listes de mots sont séparées par des virgules, comme:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

vous devez faire quelques tours supplémentaires ( forboucles):

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Sortie cette fois:

four
five,six
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.