Vérifiez si toutes les lignes du fichier se trouvent dans un fichier différent


14

J'ai obtenu deux fichiers: file1 avec environ 10 000 lignes et file2 avec quelques centaines de lignes. Je veux vérifier si toutes les lignes de file2 se trouvent dans file1. Soit: ∀ ligne ℓ ∈ fichier2: ℓ ∈ fichier1

Si quelqu'un ne sait pas ce que ces symboles signifient ou ce que "vérifier si toutes les lignes du fichier2 se produisent dans le fichier1" signifie: Plusieurs lignes équivalentes dans les deux fichiers n'influencent pas si la vérification retourne que les fichiers répondent ou non à l'exigence.

Comment puis-je faire cela?


2
Ces fichiers peuvent-ils avoir des lignes dupliquées? Si file2contient 2 lignes A, devez-vous file1contenir au moins 2 lignes A?
Stéphane Chazelas

2
@ StéphaneChazelas Toutes les lignes (dans les deux fichiers) sont garanties uniques.
UTF-8 du

1
@ UTF-8 Ce serait un détail important à modifier dans votre question.
David Z

2
@DavidZ Ce n'est plus le cas car les réponses existantes ne reposent pas sur cette garantie. Donc, en modifiant la question maintenant, je réduirais la portée apparente des réponses.
UTF-8

@ UTF-8 Je suppose que oui, bien que la question soit un peu ambiguë sans elle, par exemple si une ligne donnée apparaît 5 fois dans le fichier 2, cette ligne doit-elle également apparaître 5 fois dans le fichier 1 (par opposition à une seule fois)? Si vous aviez cette exigence, il ne semble pas que l'une des réponses existantes fonctionnerait, donc je suggère au moins de modifier quelque chose qui indique clairement que ce n'est pas ce que vous voulez dire.
David Z

Réponses:


18
comm -13 <(sort -u file_1) <(sort -u file_2)

Cette commande produira des lignes uniques à file_2. Donc, si la sortie est vide, toutes les file_2lignes sont contenues dans le file_1.

De l'homme de communication:

   With  no  options,  produce  three-column  output.  Column one contains
   lines unique to FILE1, column two contains lines unique to  FILE2,  and
   column three contains lines common to both files.

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

@don_crissti True. Correction: l' -uoption ajoutée à la sortcommande. Désormais, seules les lignes uniques sont laissées dans les deux fichiers triés.
MiniMax

Génialement simplement une solution! Cette syntaxe est-elle applicable à tout programme qui attend des fichiers? J'ai toujours pensé que la <canalisation dans stdin. Le terme de parenthèse change-t-il cela?
UTF-8 du

2
@ UTF-8 Il s'agit de la substitution de processus . Vous pouvez lire ici à ce sujet. Et oui, il se comporte comme un fichier temporaire, il peut donc être utilisé à la place de vrais fichiers dans n'importe quel programme, qui attend des fichiers.
MiniMax

Si c'est quelque chose que vous faites souvent, vous voudrez peut-être le stocker file_1sous forme triée. Économise à la fois la frappe et le temps.
Stig Hemmer

7
@minimax Bon commentaire sauf pour le "tout". La substitution de processus, bien que merveilleuse, ne peut pas être utilisée dans tous les cas, car les "fichiers" résultants sont des flux et non de vrais fichiers. Cela signifie qu'ils ne sont pas "recherchables" comme un fichier normal, et ne peuvent être utilisés que lorsque le programme lit le fichier normalement depuis le début, et non lorsque le programme utilise certaines fonctionnalités de fichier uniquement telles que la recherche vers un point spécifique ou rembobinage pour recommencer depuis le début. Heureusement, la plupart des programmes lisent simplement () leurs fichiers, et donc la substitution de processus fonctionne avec la plupart des programmes, mais pas avec "tous" les programmes.
Loi29

7
[ $(grep -cxFf file2 <(sort -u file1)) = $(sort -u file2 | wc -l) ] && 
  echo all there || 
  echo some missing

Si le nombre de correspondances de file2 dans (les lignes uniques de) file1 est le même que le nombre de lignes uniques dans file2, alors ils sont tous là; sinon, ils ne le sont pas.


5

Utiliser GNU awkoù il prend en charge une length(array)fonctionnalité spécifique (et une autre awkimplémentation qui peut prendre en charge) et non requis si les fichiers sont triés.

gawk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{print (!length(seen))?"Matched":"Not Matched"}' file2 file1

Cela lit file2 dans un tableau appelé seenavec la clé comme ligne entière de file2 .

Ensuite, lisez le fichier1 et pour chaque ligne si elle correspond aux lignes du tableau, puis supprimez cette clé.

À la fin, si le tableau était vide, toutes les lignes du fichier2 existent dans le fichier1 et s'imprimeront Matched, sinon elles s'afficheront Not Matched.


Pour la compatibilité dans toutes les awkimplémentations.

awk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{for(x in seen);print (!x)?"Matched":"Not Matched"}' file2 file1

Pour ignorer les lignes vides / ou les lignes avec des espaces blancs uniquement si dans le fichier2 , vous devrez ajouter NFà la condition dans NR==FNR && NF {...pour ignorer leur lecture dans le tableau.


length(array)est AFAIK gawk-only; ce n'est certainement pas POSIX.
dave_thompson_085

@ dave_thompson_085 Correct, j'ai mis à jour ma réponse. merci
αғsнιη

3

À l'aide de, commvous pouvez trouver des lignes communes aux deux fichiers.

comm -12 file1 file2

Jetez un oeil à man commpour plus de détails


Corrigez qu'il renvoie des lignes communes dans les deux fichiers, mais cela ne répond pas à la question de l'OP où, si vous aviez une ligne dans le fichier2 qui ne se termine pas dans le fichier1, toutes les lignes du fichier2 n'existent donc pas dans le fichier1.
αғsнιη

1
les fichiers doivent être triés. From man " comm- comparer deux fichiers triés ligne par ligne".
MiniMax du

@MiniMax a raison. Ça ne marche pas. L'autre réponse utilisant commcontient une solution qui n'est évidemment pas incorrecte. Lorsque j'exécute votre commande, j'obtiens des avertissements que les fichiers ne sont pas triés et beaucoup de lignes qui se trouvent définitivement dans les deux fichiers.
UTF-8

3
diff -q <(sort -u file2) <(grep -Fxf file2 file1 | sort -u)

produira aucune sortie si file1contient toutes les lignes dans file2et avec le statut de sortie 0, sinon il imprime quelque chose comme

Files /proc/self/fd/11 and /proc/self/fd/12 differ

et sortir avec le statut 1


2

Utilisez un programme Python:

#!/usr/bin/env python3
import sys

def open_arg(path):
    return sys.stdin if path == '-' else open(path)

def strip_linebreak(s):
    return s[:-1] if s.endswith('\n') else s

with open_arg(sys.argv[1]) as pattern_file:
    patterns = set(map(strip_linebreak, pattern_file))

with open_arg(sys.argv[2]) as dataset_file:
    for l in map(strip_linebreak, dataset_file):
        patterns.remove(l)
        if not patterns:
            break

sys.exit(int(bool(patterns)))

Usage:

python3 contains-all.py file2 file1

L'état de sortie du programme indique si tous les modèles du fichier 2 ont été mis en correspondance:

  • 0 (succès) signifie que tous les modèles ont été appariés.
  • 1 (échec) signifie que certains modèles ne correspondent pas.

Pour interroger l'état de sortie dans un shell (script) , vous pouvez utiliser la $?variable spéciale ou d' autres expressions qui le statut de sortie de commande, par exemple les opérateurs de court-circuit &&et ||et des expressions conditionnelles comme ifou while. Exemple:

if python3 compare-all.py file2 file1 && some-other --condition; then
    # do stuff
fi

1

combinede moreutils vous montrera toutes les lignes file2qui ne sont pas en accordfile1 avec:

combine file2 not file1

Ensuite, vous pouvez compter le nombre de lignes en le redirigeant vers wc -l, comme:

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
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.