182 votes

Suppression des lignes d'un fichier qui se trouvent dans un autre fichier

J'ai un fichier f1 :

line1
line2
line3
line4
..
..

Je veux supprimer toutes les lignes qui sont dans un autre fichier. f2 :

line2
line8
..
..

J'ai essayé quelque chose avec cat y sed ce qui n'est pas du tout ce que je voulais. Comment puis-je faire cela ?

5 votes

0 votes

Si vous cherchez à supprimer les lignes d'un fichier qui "contiennent même" des chaînes de caractères d'un autre fichier (par exemple, des correspondances partielles), voir unix.stackexchange.com/questions/145079/

233voto

gabuzo Points 3181

grep -v -x -f f2 f1 devrait faire l'affaire.

Explication :

  • -v pour sélectionner les lignes non correspondantes
  • -x pour ne correspondre qu'à des lignes entières
  • -f f2 pour obtenir des modèles de f2

On peut à la place utiliser grep -F o fgrep pour correspondre Cordes fixes de f2 plutôt que motifs (au cas où vous voudriez enlever les lignes d'une manière "ce que vous voyez est ce que vous obtenez" plutôt que de traiter les lignes en f2 comme des motifs regex).

29 votes

Cette opération est d'une complexité O(n²) et prendra des heures dès que les fichiers contiendront plus de quelques K lignes.

18 votes

Déterminer quels algorythmes suggérés par le SO ont une complexité O(n^2) n'a qu'une complexité O(n), mais peut encore prendre des heures à concourir.

2 votes

Je viens d'essayer sur 2 fichiers de ~2k lignes chacun, et le système d'exploitation l'a tué (il s'agit d'une VM pas très puissante, mais quand même).

85voto

Essayez plutôt comm (en supposant que f1 et f2 sont "déjà triés")

comm -2 -3 f1 f2

5 votes

Je ne suis pas sûr. comm est la solution a la question n'indique pas que les lignes en f1 sont triés, ce qui est une condition préalable à l'utilisation de l'outil comm

1 votes

Cela a fonctionné pour moi, car mes fichiers étaient triés et avaient plus de 250 000 lignes dans l'un d'eux, et seulement 28 000 dans l'autre. Merci !

3 votes

Lorsque cela fonctionne (les fichiers d'entrée sont triés), c'est extrêmement rapide !

28voto

arnaud576875 Points 35281

Si l'ordre des lignes dans la sortie n'a pas d'importance, cela fonctionne :

sort file_a file_b|uniq -u

Cela trie les deux fichiers, puis uniq peut facilement trouver les doublons et les supprimer.

El -u sur le uniq moyens de commandement remove duplicates .

La complexité du triage est de O(n.log(n)) (en supposant que sort utilise quicksort) et O(n) pour la suppression des doublons sur les lignes triées, donc ce sera plus rapide que la version grep (O(n²)) si les fichiers sont volumineux. La version de grep ressemble à ceci :

for each line l1 in f1
    for each line l2 in f2
        if l1 == l2
            break
        end if
    end for
    output l1 if no l2 == l1
endfor

Voici un benchmark rapide avec 15K entrées en f1 et 7.5K en f2 :

# add lines with number from 1 to 15000; randomly sorted; to f1
for i in $(seq 1 15000); do echo "$i"; done|sort -R > f1
# add lines with number from 1 to 15000 (with step 2); randomly sorted; to f2
for i in $(seq 1 2 15000); do echo "$i"; done|sort -R > f2

# sort|uniq -u method:
$ time sort f1 f2|uniq -u > /dev/null
real    0m0.067s
user    0m0.064s
sys     0m0.004s

# comm method (requires to sort both files separately first):
time (sort f1 > f1.sorted; sort f2 > f2.sorted; comm -2 -3 f1.sorted f2.sorted) > /dev/null
real    0m0.070s
user    0m0.068s
sys     0m0.000s

# grep method:
time grep -v -x -f f2 f1 > /dev/null
real    0m16.528s
user    0m16.457s
sys     0m0.048s

Ainsi, le sort|uniq ne prend que 0.07s tandis que le grep version prend plus de 16s et ce temps augmentera de façon exponentielle avec la taille des fichiers.

19voto

Dennis Williamson Points 105818

Pour les fichiers d'exclusion qui ne sont pas trop volumineux, vous pouvez utiliser les tableaux associatifs de AWK.

awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt 

La sortie sera dans le même ordre que le fichier "from-this.txt". Le site tolower() le rend insensible à la casse, si vous en avez besoin.

La complexité algorithmique sera probablement O(n) (taille de exclude-these.txt) + O(n) (taille de from-this.txt)

0 votes

Pourquoi dites-vous que les fichiers ne sont pas trop volumineux ? La crainte ici est (je suppose) qu'awk épuise la mémoire système pour créer le hachage, ou y a-t-il une autre limitation ?

0 votes

Pour les suiveurs, il existe même d'autres options plus agressives pour "nettoyer" les lignes (puisque la comparaison doit être exacte pour utiliser le tableau associatif), par exemple unix.stackexchange.com/a/145132/8337

0 votes

@rogerdpack : Un grand fichier d'exclusion nécessitera un grand tableau de hachage (et un long temps de traitement). Un grand fichier "from-this.txt" ne nécessitera qu'un long temps de traitement.

5voto

kurumi Points 10096

Si vous avez Ruby (1.9+)

#!/usr/bin/env ruby 
b=File.read("file2").split
open("file1").each do |x|
  x.chomp!
  puts x if !b.include?(x)
end

Ce qui a une complexité O(N^2). Si vous voulez vous soucier des performances, voici une autre version

b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}

qui utilise un hash pour effectuer la soustraction, donc de complexité O(n) (taille de a) + O(n) (taille de b)

voici un petit benchmark, avec l'aimable autorisation de l'utilisateur576875, mais avec 100K lignes, de ce qui précède :

$ for i in $(seq 1 100000); do echo "$i"; done|sort --random-sort > file1
$ for i in $(seq 1 2 100000); do echo "$i"; done|sort --random-sort > file2
$ time ruby test.rb > ruby.test

real    0m0.639s
user    0m0.554s
sys     0m0.021s

$time sort file1 file2|uniq -u  > sort.test

real    0m2.311s
user    0m1.959s
sys     0m0.040s

$ diff <(sort -n ruby.test) <(sort -n sort.test)
$

diff a été utilisé pour montrer qu'il n'y a pas de différences entre les 2 fichiers générés.

1 votes

Cette opération est d'une complexité O(n²) et prendra des heures dès que les fichiers contiendront plus de quelques K lignes.

0 votes

Je ne m'en soucie pas vraiment à ce stade, car il n'a pas mentionné de gros fichiers.

3 votes

Il n'est pas nécessaire d'être sur la défensive, ce n'est pas comme si @user576875 avait rétrogradé votre réponse ou quoi que ce soit :-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X