Durchlaufen Sie die Zeilen von zwei Dateien parallel [closed]

18

Das Ziel des von mir erstellten Skripts ist der Vergleich zweier Dateireihen. Die Dateinamen werden selbst in zwei separaten Dateien gespeichert, einem Pfad pro Zeile. Meine Idee ist, zwei while readSchleifen zu haben , eine für jede Liste von Dateinamen, aber wie kann ich die beiden Schleifen zusammenmischen?

while read compareFile <&3; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile" _(other file from loop?_) >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt

Ich muss in der Lage sein, Dateien aus zwei verschiedenen Listen gleichzeitig durch zwei Leseschleifen zu vergleichen ... Ist das überhaupt möglich?

mkrouse
quelle
Wollen Sie dieses Drehbuch zum König der Herausforderungen machen? Wenn nicht, gibt es bereits leistungsfähige Tools zum Vergleichen von Dateien, z diff.
Lgeorget
" Art der Herausforderung", sorry
lgeorget
@lgeorget das OP verwendet diff.
Terdon
Ah, Dateien aus zwei Listen. Sorry für die nutzlosen Kommentare ...
lgeorget
Bitte vermeiden Sie Cross-Posting
iruvar

Antworten:

20

Sie brauchen keine zwei Schleifen; Sie müssen nur aus zwei Dateien in einer Schleife lesen.

while read compareFile1 <&3 && read compareFile2 <&4; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile1" "$compareFile2" >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt 4<other_file
Psusi
quelle
das ist viel mehr weniger Code danke! Wie gehe ich dann mit einer Leerzeilenausnahme für die beiden Schleifen gleichzeitig um?
mkrouse
@mkrouse, ich weiß nicht, was Sie mit dieser $ server-Variablen gemacht haben, aber wie auch immer Sie auf eine leere Zeile für die eine Variable testen, machen Sie dasselbe für die andere ...
psusi
7

Methode 1: Verwenden Sie, was Sie wissen

Da Sie bereits wissen, wie man eine Datei durchläuft, können Sie die Dateien kombinieren und dann die kombinierten Dateien verarbeiten. Der Befehl pasteverbindet zwei Dateien zeilenweise. Es wird ein Tabulator zwischen den Zeilen eingefügt, die aus den beiden Dateien stammen. Bei dieser Lösung wird davon ausgegangen, dass Ihre Dateinamen keine Tabulatoren enthalten. (Sie können das Trennzeichen ändern, müssen jedoch ein Zeichen suchen, das in einem Dateinamen nicht vorhanden ist.)

paste -- "$list1.txt" "list2.txt" |
while IFS=$'\t' read -r file1 file2 rest; do
  diff -q -- "$file1" "$file2"
  case $? in
    0) status='same';;
    1) status='different';;
    *) status='ERROR';;
  esac
  echo "$status $file1 $file2"
done

Wenn Sie leere Zeilen überspringen möchten, müssen Sie dies in jeder Datei separat tun, da pastemöglicherweise eine leere Zeile aus einer Datei mit einer nicht leeren Zeile aus einer anderen Datei abgeglichen wird. Mit können Sie grepdie nicht leeren Zeilen filtern.

paste -- <(grep '[^[:space:]]' "$list1.txt") <(grep '[^[:space:]]' "list2.txt") |
while IFS=$'\t' read -r file1 file2 rest; do
  

Beachten Sie, dass bei unterschiedlichen Längen der beiden Dateien eine leere angezeigt wird $file2(unabhängig davon, welche Liste zuerst beendet wurde).

Methode 2: Schleife über zwei Dateien

Sie können einen Befehl im Zustand der while-Schleife so komplex setzen, wie Sie möchten. Wenn Sie setzen read file1 <&3 && read file2 <&4, läuft die Schleife so lange, wie beide Dateien eine Zeile zum Lesen haben, dh bis eine Datei leer ist.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

Wenn Sie leere Zeilen überspringen möchten, ist dies etwas komplizierter, da Sie die beiden Dateien unabhängig voneinander überspringen müssen. Die einfache Möglichkeit besteht darin, das Problem in zwei Teile zu unterteilen: Überspringen Sie die Leerzeilen aus einer Datei und verarbeiten Sie die nicht leeren Zeilen. Eine Methode zum Überspringen der Leerzeilen besteht darin, grepwie oben beschrieben vorzugehen. Achten Sie auf den erforderlichen Abstand zwischen dem <Umleitungsoperator und dem Operator <(, der eine Befehlssuspension startet.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3< <(grep '[^[:space:]]' "$list1.txt") 4< <(grep '[^[:space:]]' "list2.txt")

Eine andere Methode besteht darin, eine Funktion zu schreiben, die sich so verhält, readaber leere Zeilen überspringt. Diese Funktion kann durch Aufrufen readeiner Schleife ausgeführt werden. Es muss keine Funktion sein, aber eine Funktion ist der beste Ansatz, um Ihren Code zu organisieren und weil dieser Code zweimal aufgerufen werden muss. In der Funktion gibt ${!#}es eine Instanz des bash-Konstrukts, ${!VARIABLE}das den Wert der Variablen ergibt, deren Name der Wert von ist VARIABLE. hier ist die Variable die spezielle Variable, #die die Nummer des Positionsparameters enthält, so ${!#}wie der letzte Positionsparameter.

function read_nonblank {
  while read "$@" &&
        [[ ${!#} !~ [^[:space:]] ]]
  do :; done
}
while read_nonblank -u 3 -r file1 && read_nonblank -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt
Gilles 'SO - hör auf böse zu sein'
quelle
Ich mag die Verwendung von Reads -uOption
Felipe Alvarez
1

Ein Ansatz wäre, read -rastatt nur zu verwenden read. Angenommen, die filestoCompare.txtenthaltenen 2 Spalten mit den jeweiligen Dateinamen read -ralesen beide Spalten gleichzeitig ein und weisen sie einem Array zu.compareFile . Auf dieses Array könnte dann zugegriffen werden, so dass der Index 0 die erste Datei und der Index 1 jedes Mal die zweite Datei ist, wenn die whileSchleife durchlaufen wird.

Beispiel

Angenommen, ich habe diese Datei: filestoCompare.txtund sie enthält Folgendes:

file1 file2
file3 file4
file5 file6

Der Befehl zum Durchlaufen dieser Datei lautet wie folgt:

$ while read -ra a ; do printf "%s\t%s\n" ${a[0]} ${a[1]}; done < filestoCompare.txt
file1   file2
file3   file4
file5   file6

Wenn die 2 Dateien in der Tat separate Dateien sind, wie zum Beispiel:

#list1
file1
file2
file3

#list2
file4
file5
file6

Sie können wie folgt mit dem pasteBefehl verbunden werden:

$ paste list1 list2 > list1and2

Hier ist der Inhalt von list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6
slm
quelle
Dies ist jedoch nicht das Eingabeformat: Die Listen befinden sich in zwei verschiedenen Dateien. Du könntest joinsie zuerst.
Gilles 'SO- hör auf böse zu sein'
@ Gilles - Ich weiß, dass dies nicht das Eingabeformat ist. Ich glaube, ich habe sogar gesagt, dass "... Angenommen, die Datei" filestoCompare.txt "enthielt 2 Spalten mit den jeweiligen Dateinamen ...". Ich verstehe Ihre Behauptung und bin nicht anderer Meinung. Das OP hat seit seiner Veröffentlichung keine weiteren Hinweise zu dieser Frage gegeben.
SLM
@ Gilles - was ist, wenn ich ein Beispiel hinzufüge, das zeigt, wie der Befehl pastezum Verknüpfen der 2 Dateien verwendet wird? Würde dich das dazu bringen, deine Stimme zu widerrufen?
SLM