2014-06-20 15 views
11

Mam dwa pliki nie są sortowane, które mają pewne wspólne linie.Jak usunąć wspólne linie między dwoma plikami bez sortowania?

plik1.txt

Z 
B 
A 
H 
L 

file2.txt

S 
L 
W 
Q 
A 

Sposób używam usunąć wspólnych linii jest następujący:

sort -u file1.txt > file1_sorted.txt 
sort -u file2.txt > file2_sorted.txt 

comm -23 file1_sorted.txt file2_sorted.txt > file_final.txt 

wyjściowa:

B 
H 
Z 

Problemem jest to, że chcę zachować kolejność file1.txt, to znaczy:

pożądany wynik:

Z 
B 
H 

Jedno rozwiązanie myślałam robi pętlę, aby przeczytać wszystkie linie plik2 .txt i:

sed -i '/^${line_file2}$/d' file1.txt 

Ale jeśli pliki są duże, wydajność może się wyssać.

  • Czy podoba Ci się mój pomysł?
  • Czy masz jakąś alternatywę, aby to zrobić?

Odpowiedz

12

grep lub awk:

awk 'NR==FNR{a[$0]=1;next}!a[$0]' file2 file1 
+0

'a [0 0] = 7' Dlaczego równa się siedem? Dzięki! :) – harrison4

+2

@JohnDoe potrzebujemy niezerowej liczby, 7 i 1 nie mają różnicy. Zmieniam go na 1, jeśli sprawi, że poczujesz się komfortowo. :-) – Kent

+0

Tak, teraz czuję się znacznie lepiej. :) – harrison4

10

Możesz użyć tylko grep (-v dla odwrócenia, -f dla pliku). Linie grep z input1 które nie pasują do żadnej linii w input2:

grep -vf input2 input1 

Daje:

Z 
B 
H 
+4

byłoby lepiej, gdyby z opcją '-F -w lub -x'? na przykład podłańcuch. – Kent

3

Pisałem trochę skrypt Perl którego używam do tego rodzaju rzeczy. To może zrobić więcej niż co prosisz, ale może to również zrobić to, czego potrzebujesz:

#!/usr/bin/env perl -w 
use strict; 
use Getopt::Std; 
my %opts; 
getopts('hvfcmdk:', \%opts); 
my $missing=$opts{m}||undef; 
my $column=$opts{k}||undef; 
my $common=$opts{c}||undef; 
my $verbose=$opts{v}||undef; 
my $fast=$opts{f}||undef; 
my $dupes=$opts{d}||undef; 
$missing=1 unless $common || $dupes;; 
&usage() unless $ARGV[1]; 
&usage() if $opts{h}; 
my (%found,%k,%fields); 
if ($column) { 
    die("The -k option only works in fast (-f) mode\n") unless $fast; 
    $column--; ## So I don't need to count from 0 
} 

open(my $F1,"$ARGV[0]")||die("Cannot open $ARGV[0]: $!\n"); 
while(<$F1>){ 
    chomp; 
    if ($fast){ 
    my @aa=split(/\s+/,$_); 
    $k{$aa[0]}++; 
     $found{$aa[0]}++; 
    } 
    else { 
    $k{$_}++; 
     $found{$_}++; 
    } 
} 
close($F1); 
my $n=0; 
open(F2,"$ARGV[1]")||die("Cannot open $ARGV[1]: $!\n"); 
my $size=0; 
if($verbose){ 
    while(<F2>){ 
     $size++; 
    } 
} 
close(F2); 
open(F2,"$ARGV[1]")||die("Cannot open $ARGV[1]: $!\n"); 

while(<F2>){ 
    next if /^\s+$/; 
    $n++; 
    chomp; 
    print STDERR "." if $verbose && $n % 10==0; 
    print STDERR "[$n of $size lines]\n" if $verbose && $n % 800==0; 
    if($fast){ 
     my @aa=split(/\s+/,$_); 
     $k{$aa[0]}++ if defined($k{$aa[0]}); 
     $fields{$aa[0]}=\@aa if $column; 
    } 
    else{ 
     my @keys=keys(%k); 
     foreach my $key(keys(%found)){ 
      if (/\Q$key/){ 
      $k{$key}++ ; 
      $found{$key}=undef unless $dupes; 
      } 
     } 
    } 
} 
close(F2); 
print STDERR "[$n of $size lines]\n" if $verbose; 

if ($column) { 
    $missing && do map{my @[email protected]{$fields{$_}}; print "$aa[$column]\n" unless $k{$_}>1}keys(%k); 
    $common && do map{my @[email protected]{$fields{$_}}; print "$aa[$column]\n" if $k{$_}>1}keys(%k); 
    $dupes && do map{my @[email protected]{$fields{$_}}; print "$aa[$column]\n" if $k{$_}>2}keys(%k); 
} 
else { 
    $missing && do map{print "$_\n" unless $k{$_}>1}keys(%k); 
    $common && do map{print "$_\n" if $k{$_}>1}keys(%k); 
    $dupes && do map{print "$_\n" if $k{$_}>2}keys(%k); 
} 
sub usage{ 
    print STDERR <<EndOfHelp; 

    USAGE: compare_lists.pl FILE1 FILE2 

     This script will compare FILE1 and FILE2, searching for the 
     contents of FILE1 in FILE2 (and NOT vice versa). FILE one must 
     be one search pattern per line, the search pattern need only be 
     contained within one of the lines of FILE2. 

    OPTIONS: 
     -c : Print patterns COMMON to both files 
     -f : Search only the first characters of each line of FILE2 
     for the search pattern given in FILE1 
     -d : Print duplicate entries  
     -m : Print patterns MISSING in FILE2 (default) 
     -h : Print this help and exit 
EndOfHelp 
     exit(0); 
} 

W twoim przypadku, należy uruchomić go jako

list_compare.pl -cf file1.txt file2.txt 

Opcja -f czyni go porównać tylko pierwsze słowo (zdefiniowane przez białą spację) pliku 2 i znacznie przyspiesza działanie. Aby porównać całą linię, usuń -f.