• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[solved] mit bash 2 Listen vergleichen

admine

Ultimate Guru
Hallo Leute,

ich brauch mal wieder etwas Bash-Hilfe ;)

2 Listen sollen miteinander verglichen werden.
Das besondere dabei ist, dass sie nicht zeilenweise übereinstimmen müssen, sondern "nur" inhaltlich.
Also jede Zeile der einen Liste muss in der 2. Liste auch sein, aber egal an welcher Position der Liste.
=> diff hilft mir nicht.
Wird eine Zeile aus Liste 1 in Liste 2 nicht gefunden, dann soll mir diese Zeile in eine Fehler-Datei geschrieben werden.

Folgendes hab ich bisher:
Code:
#!/bin/bash                                         

set -x

#Fehlerliste löschen
rm fehler.txt

echo "Prüfliste: "
read P1
echo "Kontrollliste: "
read K1

#control (){
# for k in `cat $K1`
#  do
#  echo $k
# done
#}

#for i in `cat $P1`
#  do
#   echo $i
#   [ "{$i}" == control ]
#     if [ $? -eq 1 ] ; then
#        echo $i >> fehler.txt
#     fi
#
#done

while
read line; do
  echo $line;

   [ $line == `cat $K1` ]
     if [ $? -eq 1 ] ; then
        echo $line >> fehler.txt
     fi

done < $P1
Mein Problem ist jetzt, dass Liste 1 schön zeilenweise ausgelesen wird, aber Liste 2 nicht.
Da wird der gesamte Inhalt getestet und das geht natürlich schief :(
Es soll aber gegen jede Zeile der Liste 2 getestet werden und nur wenn gar keine Übereinstimmung ist, die fehler.txt gefüllt werden.

Wie kann ich sowas bewerkstelligen?
Mir fehlen grad die Ideen :???:

Danke!
 
Ich bin nicht der Held der Bash und würde die Dateien auslesen, mit sort sortieren und jeweils in 'ne neue Datei schreiben lassen und diese dann vergleichen, natürlich mit diff.
 
OP
A

admine

Ultimate Guru
Geier0815 schrieb:
Ich bin nicht der Held der Bash und würde die Dateien auslesen, mit sort sortieren und jeweils in 'ne neue Datei schreiben lassen und diese dann vergleichen, natürlich mit diff.
Diese Alternative kam mir auch schon ein. ;)
Aber ich dachte eben, es geht evt. auch ohne Umweg.
 
A

Anonymous

Gast
Auch mal hier vorbeischauen, so ein ähnliches Thema hatten wir schon mal.
http://www.linux-club.de/viewtopic.php?f=21&t=105845

Code:
awk '{x[$0]+=1};END{for(i in x){if(x[i]==1)print i}}' liste1   liste2
würden demnach alles ausgeben was in (liste1 + liste2) insgesamt nur ein mal vorkommt.

robi
 
OP
A

admine

Ultimate Guru
Hallo oc2pus, Hallo robi,

danke für die Tipps ... ich schau mir das mal an und werde berichten.
 
OP
A

admine

Ultimate Guru
Also ... momentan favorisiere ich die "grep"-Variante.

awk arbeitet zwar wunderbar, aber ich bekomme dann eben alle Ergebnisse und kann nicht unterscheiden in welcher Liste der Eintrag fehlt bzw. zuviel ist.
Müsste ich dann also noch mal mit "grep" gegen beide Listen laufen lassen.

Mit "grep -vf" scheints gut zu funktionieren ... hm ... allerdings hab ich da tatsächlich auch so seltsame Erscheinungen (ähnlich, wie in dem verlinkten Thread).

Das hab ich jetzt:
Code:
#!/bin/bash

set -x

echo "Prüfliste: "
read P1
echo "Kontrollliste: "
read K1

#Fehlerliste löschen
rm fehler_in_$P1.txt
rm fehler_in_$K1.txt

grep -vf $P1 $K1 > fehler_in_$P1.txt

grep -vf $K1 $P1 > fehler_in_$K1.txt

Und hier mal ein Beispiel aus den Listen (die x gibts in den wirklichen Listen natürlich nicht ;) ):
Code:
isdn caller 0174339xxxx
isdn caller 0175162xxxx
isdn caller 0170462xxxx
isdn caller 0175584xxxx
isdn caller 01515285xxxx
isdn caller 0162252xxxx
isdn caller 01514004xxxx
isdn caller 0163882xxxx
isdn caller 01511423xxxx
Das ist aber wirklich nur ein kleiner Ausschnitt ... die Listen haben ca. 1500 Zeilen.
 
A

Anonymous

Gast
Na gut, dann müssen wir schärfere AWK-Geschosse auspacken ;)

folgendes als Script speichern und Ausführungsrechte vergeben
Code:
#!/usr/bin/awk -f
BEGIN { rulesfile="" }

rulesfile == "" { rulesfile = FILENAME; }
rulesfile == FILENAME { x[$0]+=1 }
rulesfile != FILENAME { y[$0]+=1 }

END { for (i in x){if (y[i] < 1) print i}}
Eventuell je nach LINUX-Version muss die erste Zeile auf /bin/awk geändert werden.
Ausführen dann wie folgt
Code:
./script liste1 liste2
Als Ausgabe würde ich erwarten alle Zeilen die in der liste2 gegenüber der liste1 fehlen und zwar unabhängig davon wie oft sich Zeilen in beiden Datein wiederholen. Eine Ausgabe erfolgt wenn in liste2 der Eintrag komplett fehlt.

Kleiner Gag am Rande, wenn du die liste1 mit dem gesamt Inhalt aus (liste2 + liste3 + liste4 + liste5) vergleichen willst.
Code:
./script liste1 liste2 liste3 liste4 liste5
sollte auch funktionieren. ;)

Wenn du die Gegenprobe auch noch gleich mit einem Ritt haben möchtest,
Code:
#!/usr/bin/awk -f
BEGIN { rulesfile=""}

rulesfile == "" { rulesfile = FILENAME; }
rulesfile == FILENAME { x[$0]+=1 }
rulesfile != FILENAME { y[$0]+=1 }

END { for (i in x){if (y[i] < 1) print i > "logfile1.txt"}
      for (i in y){if (x[i] < 1) print i > "logfile2.txt"}}

die beiden File in der die Ergebnisse landen sind logfile1.txt und logfile2.txt und fest im AWK-Script integriert. Sie werden dabei nur angelegt wenn es auch was reinzuschreiben gibt, gleichnamige Dateien sollten also in dieser Version vorher gelöscht werden. Wenn das Löschen awk auch noch machen soll, kein Problem, aber was machst du dann den ganzen Tag ? ;) ;) ;) ;) ;) ;) ;) ;) ;)
logfile1.txt steht was in liste2 fehlt und logfile2.txt steht was in liste1 fehlt.

robi
 

abgdf

Guru
Perl bietet eine grep-Funktion zum Durchsuchen von Perl-Listen; Dateien können in Perl-Listen eingelesen werden. Z.B.:
Code:
#!/usr/bin/perl

use warnings;
use strict;

my @a = &readfile("K1");
my @b = &readfile("P1");
chomp(@a);
chomp(@b);

my $i;

foreach $i (@b) {
    my @d = grep { $_ eq $i } @a;
    if ($#d == -1) {
        print "$i\n";
    }
    if ($#d > 0) {
        print "'$i' is " . ($#d + 1) . " times in list.\n";
    }
}

sub readfile {
    my $file = shift;
    open(FH, "<$file") or die "Error reading file '$file',";
    my @a = <FH>; 
    close(FH);
    return @a;
}
Gruß
 
OP
A

admine

Ultimate Guru
@ robi

Code:
admine:~/tmp> sh caller_check_awk.sh pruef1 kontrol1
caller_check_awk.sh: Zeile 2: BEGIN: Kommando nicht gefunden.
caller_check_awk.sh: Zeile 4: Syntaxfehler beim unerwarteten Wort `}'
caller_check_awk.sh: Zeile 4: `rulesfile == "" { rulesfile = FILENAME; }'
Was hab ich da vergessen? :roll:
 
A

Anonymous

Gast
admine schrieb:
Was hab ich da vergessen? :roll:

ist kein Shellscript ;)

geht nicht aufzurufen als "sh script optionen "
sondern nur "/PATH/script optionen" mit Ausführungsrechten auf dem Script selbst.
eventuell geht auch "awk -f script optionen" habe ich aber nicht probiert.

robi
 
OP
A

admine

Ultimate Guru
Vielen Dank robi,
das funzt super! :)

Morgen werde ich es dann dem "Härtetest" unterziehen, aber ich glaub es wird auch klappen.

Vielen Dank!!
 
robi schrieb:
Als Ausgabe würde ich erwarten alle Zeilen die in der liste2 gegenüber der liste1 fehlen

Ich wollte nur schnell erwähnen dass man die o.g. Reihenfolge einhalten muss (is ja auch logisch) denn ansonsten kommt nur Müll raus.
Ausserdem : wenn ein Eintrag in Liste2 ist aber nicht in Liste1 dann wird der letzte, in Liste2 nicht-vorhandene Eintrag ausgegeben ... das is dann irgendwie :irre: - aber das kann man ja entspr. abfangen wenns beliebt, is nur wichtig wenn es um kritische Dateilistenvergleiche geht (wie zB. bei Backups etc)
 

framp

Moderator
Teammitglied
Da bei Linux ja immer betont wird, dass es eine mächtige Sammlung von Tools beinhaltet und dieses Suchen nach Unterschieden in zwei Dateien ein häufiges Problem ist, habe ich spasseshalber mal nach diesem Tool gesucht: Und siehe da:
Code:
comm -output-delimiter ":" file1 file2 | egrep "^:"
bzw
Code:
comm -output-delimiter ":" file1 file2 | egrep "^[^:]"
liefert das Ergebnis sofern die beiden Dateien vorher mit sort sortiert wurden .

Linux ist eine Samlung von kleinen kombinierbaren Tools mit denen, geschickt kombiniert, man jedes Problem lösen kann
qed ;)
 
Oben