Pętla przez linie dwóch plików równolegle [zamknięte]


18

Celem skryptu, który tworzę, jest porównanie dwóch serii plików. Nazwy plików są przechowywane w dwóch osobnych plikach, po jednej ścieżce na wiersz. Moim pomysłem jest posiadanie dwóch while readpętli, po jednej dla każdej listy nazw plików, ale jak mogę mieszać obie pętle razem?

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

Muszę być w stanie porównywać pliki z dwóch różnych list jednocześnie przez dwa podczas odczytu pętli ... Czy to w ogóle możliwe?


Czy zamierzasz zrobić ten skrypt jako król wyzwań? Jeśli nie, istnieją już potężne narzędzia do porównywania plików diff.
lgeorget,

rodzaj wyzwania”, przepraszam
lgeorget,

@lgeorget, którego używa OP diff.
terdon

ach, pliki z dwóch list. Przepraszamy za bezużyteczne komentarze ...
lgeorget,

Odpowiedzi:


20

Nie potrzebujesz dwóch pętli; wystarczy odczytać z dwóch plików w jednej pętli.

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

to o wiele mniej dzięki dzięki! jak obsłużyć wyjątek pustej linii w tym samym czasie dla dwóch pętli?
mkrouse

@mkrouse, nie wiem co robiłeś z tą zmienną $ server tam wcześniej, ale jednak testujesz na pustej linii na jednej zmiennej, po prostu robisz to samo na drugiej ...
psusi

7

Metoda 1: użyj tego, co wiesz

Ponieważ wiesz już, jak zapętlić jeden plik, możesz połączyć pliki, a następnie przetworzyć połączone pliki. Polecenie pastełączy dwa pliki linia po linii. Umieszcza tabulator między wierszami pochodzącymi z dwóch plików, więc to rozwiązanie zakłada, że ​​w nazwach plików nie ma tabulatorów. (Możesz zmienić separator, ale musisz znaleźć znak, którego nie ma w nazwie pliku).

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

Jeśli chcesz pominąć puste linie, musisz to zrobić w każdym pliku osobno, ponieważ pastemoże on pasować do pustej linii z jednego pliku z niepustą linią z innego pliku. Możesz użyć grepdo filtrowania niepustych linii.

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

Pamiętaj, że jeśli dwa pliki mają różne długości, otrzymasz pusty $file2 (niezależnie od tego, która lista kończy się jako pierwsza).

Metoda 2: zapętlenie dwóch plików

Możesz umieścić tak złożone polecenie, jak chcesz w warstwie pętli while. Jeśli wstawisz, read file1 <&3 && read file2 <&4pętla będzie działać tak długo, jak oba pliki będą miały linię do odczytania, tj. Dopóki jeden plik się nie skończy.

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

Jeśli chcesz pominąć puste linie, jest to trochę bardziej skomplikowane, ponieważ musisz wykonać pomijanie dwóch plików niezależnie. Prostym sposobem jest podzielenie problemu na dwie części: pominięcie pustych linii z jednego pliku i przetworzenie niepustych linii. Jedną z metod pomijania pustych linii jest przetworzenie grepjak wyżej. Uważaj na niezbędną przestrzeń między <operatorem przekierowania a operatorem <(rozpoczynającym zawieszanie polecenia.

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

Inną metodą jest napisanie funkcji, która zachowuje się tak, readale pomija puste linie. Ta funkcja może działać, wywołując readpętlę. Nie musi to być funkcja, ale funkcja jest najlepszym podejściem, zarówno do uporządkowania kodu, jak i dlatego, że ten fragment kodu należy wywołać dwukrotnie. W funkcji ${!#}jest instancja konstrukcji bash, ${!VARIABLE}która ocenia wartość zmiennej, której nazwa jest wartością VARIABLE; tutaj zmienna jest specjalną zmienną, #która zawiera liczbę parametru pozycyjnego, podobnie ${!#}jak ostatni parametr pozycyjny.

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

Podoba mi się użycie -uopcji odczytu
Felipe Alvarez

1

Jednym z podejść byłoby użycie read -razamiast sprawiedliwego read. Zakładając, że filestoCompare.txtzawarte 2 kolumny z nazwami plików w każdy, może read -rabyłoby przeczytać obie kolumny w tym samym czasie i przypisać je do tablicy, compareFile. Następnie można uzyskać dostęp do tej tablicy, aby indeks 0 był pierwszym plikiem, a indeks 1 drugim plikiem za każdym razem przezwhile pętlę.

Przykład

Powiedz, że mam ten plik: filestoCompare.txti zawiera on:

file1 file2
file3 file4
file5 file6

Polecenie przejścia przez ten plik będzie wyglądało następująco:

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

Jeśli 2 pliki są rzeczywiście osobnymi plikami, takimi jak:

#list1
file1
file2
file3

#list2
file4
file5
file6

Można je łączyć razem z pastepoleceniem w następujący sposób:

$ paste list1 list2 > list1and2

Oto zawartość list1 i 2:

$ cat list1and2
file1   file4
file2   file5
file3   file6

Ale to nie jest format wejściowy: listy są w dwóch różnych plikach. Mógłbyś joinje pierwszy.
Gilles 'SO - przestań być zły'

@Gilles - Wiem, że to nie jest format wejściowy. Wydaje mi się, że nawet powiedziałem: „... Zakładając, że plik filestoCompare.txt zawierał 2 kolumny z nazwami plików w każdym…”. Rozumiem twoje twierdzenie i nie zgadzam się. Od czasu opublikowania PO nie udzielił żadnych dalszych wskazówek na ten temat.
slm

@Gilles - co jeśli dodam przykład pokazujący, jak użyć polecenia, pasteaby połączyć 2 pliki? Czy to sprawi, że nie będziesz głosować?
slm
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.