Comment savoir si un fichier texte est un sous-ensemble d'un autre

12

J'essaie de trouver un moyen de déterminer si un fichier texte est un sous-ensemble d'un autre ..

Par exemple:

foo
bar

est un sous-ensemble de

foo
bar
pluto

Tandis que:

foo
pluto

et

foo
bar

ne sont pas un sous-ensemble les uns des autres ...

Existe-t-il un moyen de le faire avec une commande?

Ce chèque doit être un contre-chèque et il doit renvoyer:

file1 subset of file2 :    True
file2 subset of file1 :    True
otherwise             :    False
gc5
la source
Solution potentiellement plus efficace (si des fichiers sont également commandés): github.com/barrycarter/bcapps/blob/master/…
barrycarter

Réponses:

11

Si les contenus de fichiers sont appelés file1, file2et file3dans l' ordre de apearance vous pouvez le faire avec la seule ligne suivante:

 # python -c "x=open('file1').read(); y=open('file2').read(); print x in y or y in x"
 True
 # python -c "x=open('file2').read(); y=open('file1').read(); print x in y or y in x"
 True
 # python -c "x=open('file1').read(); y=open('file3').read(); print x in y or y in x"
 False
Timo
la source
Merci pour votre réponse .. +1 .. Je ne sais pas si j'accepte ma réponse car la vôtre n'est pas spécifique à unix-linux et ma réponse est un peu plus rapide, pour autant que je l'ai testée .. qu'en pensez-vous?
gc5
Vous êtes les bienvenus, il existe bien sûr d'autres solutions avec des outils plus spécifiques à Unix. Mais cela semble être une bonne utilisation de l' inopérateur de Python .
Timo
Il y a un wrapper de ligne de commande python pour le rendre plus unix, avec une tuyauterie intégrée, nommée pyp: code.google.com/p/pyp Je pense qu'il est trivial de rendre cette solution plus unix comme un outil liner.
IBr
3

Avec perl:

if perl -0777 -e '$n = <>; $h = <>; exit(index($h,$n)<0)' needle.txt haystack.txt
then echo needle.txt is found in haystack.txt
fi

-0octaldéfinit le délimiteur d'enregistrement. Lorsque ce nombre octal est supérieur à 0377 (la valeur d'octet maximale), cela signifie qu'il n'y a pas de délimiteur, c'est équivalent à faire $/ = undef. Dans ce cas, <>renvoie le contenu complet d'un seul fichier, c'est le mode slurp .

Une fois que nous avons le contenu des fichiers en deux $het $nvariables, nous pouvons utiliser index()pour déterminer si l'un se trouve dans l'autre.

Cela signifie cependant que tous les fichiers sont stockés en mémoire, ce qui signifie que cette méthode ne fonctionnera pas pour les fichiers très volumineux.

Pour les fichiers mmappables (inclut généralement les fichiers standard et les fichiers les plus recherchés comme les périphériques de bloc), cela peut être contourné en utilisant mmap()les fichiers, comme avec le Sys::Mmapmodule perl:

if 
  perl -MSys::Mmap -le '
    open N, "<", $ARGV[0] || die "$ARGV[0]: $!";
    open H, "<", $ARGV[1] || die "$ARGV[1]: $!";
    mmap($n, 0, PROT_READ, MAP_SHARED, N);
    mmap($h, 0, PROT_READ, MAP_SHARED, H);
    exit (index($h, $n) < 0)' needle.txt haystack.txt
then
  echo needle.txt is found in haystack.txt
fi
Stéphane Chazelas
la source
2

J'ai trouvé une solution grâce à cette question

Fondamentalement, je teste deux fichiers a.txtet b.txtavec ce script:

#!/bin/bash

first_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$1" "$2" | wc -l)
second_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$2" "$1" | wc -l)

if [ "$first_cmp" -eq "0" -o "$second_cmp" -eq "0" ]
then
    echo "Subset"
    exit 0
else
    echo "Not subset"
    exit 1
fi

Si l'un est un sous-ensemble de l'autre, le script revient 0pour le Truecontraire 1.

gc5
la source
Que fait% L? Ce script ne semble pas fonctionner, et j'essaie de le déboguer ...
Alex
En fait, je ne me souviens pas de la signification de %L, c'était il y a trois ans. De man diff(version actuelle) %Lsignifie "contenu de la ligne".
gc5
% L imprime le contenu de la "nouvelle" ligne. IOW, n'imprime rien pour les lignes inchangées ou les anciennes lignes, mais imprime le contenu de la ligne pour les nouvelles lignes.
PLG
Ce script fonctionne pour moi, hors de la boîte!
PLG
2

Si f1 est un sous-ensemble de f2, alors f1 - f2 est un ensemble vide. En s'appuyant sur cela, nous pouvons écrire une fonction is_subset et une fonction qui en dérive. Selon la différence entre 2 fichiers texte


sort_files () {
  f1_sorted = "$ 1.sorted"
  f2_sorted = "$ 2.sorted"

  si [ ! -f $ f1_sorted]; ensuite
    chat 1 $ | trier | uniq> $ f1_sorted
  Fi

  si [ ! -f $ f2_sorted]; ensuite
    chat 2 $ | trier | uniq> $ f2_sorted
  Fi
}

remove_sorted_files () {
  f1_sorted = "$ 1.sorted"
  f2_sorted = "$ 2.sorted"
  rm -f $ f1_sorted
  rm -f $ f2_sorted
}

set_union () {
  sort_files $ 1 $ 2
  chat "$ 1. trié" "$ 2. trié" | trier | uniq
  remove_sorted_files $ 1 $ 2
}

set_diff () {
  sort_files $ 1 $ 2
  chat "$ 1. trié" "$ 2. trié" "$ 2. trié" | trier | uniq -u
  remove_sorted_files $ 1 $ 2
}

rset_diff () {
  sort_files $ 1 $ 2
  chat "$ 1. trié" "$ 2. trié" "$ 1. trié" | trier | uniq -u
  remove_sorted_files $ 1 $ 2
}

is_subset () {
  sort_files $ 1 $ 2
  sortie = $ (set_diff $ 1 $ 2)
  remove_sorted_files $ 1 $ 2

  if [-z $ output]; ensuite
    retour 0
  autre
    retour 1
  Fi

}

Saurabh Hirani
la source
Ce script doit-il commencer par #!/bin/bash?
Alex
2

Depuis http://www.catonmat.net/blog/set-operations-in-unix-shell/ :

Comm compare deux fichiers triés ligne par ligne. Il peut être exécuté de telle sorte qu'il génère des lignes qui n'apparaissent que dans le premier fichier spécifié. Si le premier fichier est un sous-ensemble du second, alors toutes les lignes du 1er fichier apparaissent également dans le 2e, donc aucune sortie n'est produite:

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
# comm returns no output if subset ⊆ set
# comm outputs something if subset ⊊ set
Alec
la source