Passa attraverso le linee di due file in parallelo [chiuso]


18

L'oggetto dello script che sto realizzando è confrontare due serie di file. I nomi dei file sono essi stessi memorizzati in due file separati, un percorso per riga. La mia idea è di avere due while readloop, uno per ogni elenco di nomi di file, ma come posso mescolare i due loop insieme?

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

Devo essere in grado di confrontare i file da due elenchi diversi contemporaneamente attraverso due mentre si leggono i loop ... È anche possibile?


Hai intenzione di fare questo copione come un re della sfida? In caso contrario, esistono già potenti strumenti per confrontare file, come diff.
Lgeorget,

" tipo di sfida", scusa
lgeorget il

@lgeorget l'OP sta usando diff.
terdon

ah, file da due elenchi.
Ci

Si prega di evitare il cross-posting
iruvar l'

Risposte:


20

Non hai bisogno di due anelli; devi solo leggere da due file in un ciclo.

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

questo è molto meno codice grazie! come gestire un'eccezione di riga vuota contemporaneamente per i due loop?
mkrouse,

@mkrouse, non so cosa stavi facendo con quella variabile $ server lì prima, ma comunque
testerai

7

Metodo 1: usa ciò che sai

Poiché sai già come eseguire il loop su un file, puoi combinare i file e quindi elaborare i file combinati. Il comando pasteunisce due file riga per riga. Inserisce una scheda tra le linee provenienti dai due file, quindi questa soluzione presuppone che non ci siano schede nei nomi dei file. (Puoi cambiare il separatore ma devi trovare un carattere che non è presente nel nome di un file.)

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

Se si desidera saltare le righe vuote, è necessario farlo separatamente in ciascun file, poiché pastepotrebbe corrispondere a una riga vuota di un file con una riga non vuota di un altro file. È possibile utilizzare grepper filtrare le righe non vuote.

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

Nota che se i due file hanno lunghezze diverse, otterrai uno spazio vuoto $file2(indipendentemente dall'elenco terminato per primo).

Metodo 2: ciclo su due file

Puoi inserire un comando complesso come desideri nella condizione del ciclo while. Se lo metti, read file1 <&3 && read file2 <&4il ciclo funzionerà finché entrambi i file hanno una riga da leggere, cioè fino a quando un file si esaurisce.

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

Se vuoi saltare le righe vuote, è un po 'più complicato, perché devi saltare i due file in modo indipendente. Il modo più semplice è dividere il problema in due parti: saltare le righe vuote da un file ed elaborare le righe non vuote. Un metodo per saltare le righe vuote è elaborare grepcome sopra. Fai attenzione allo spazio necessario tra l' <operatore di reindirizzamento e quello <(che avvia una sospensione del comando.

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

Un altro metodo è scrivere una funzione che si comporta come readma salta le righe vuote. Questa funzione può funzionare chiamando readin un ciclo. Non deve essere una funzione, ma una funzione è l'approccio migliore, sia per organizzare il codice sia perché quel pezzo di codice deve essere chiamato due volte. Nella funzione, ${!#}è un'istanza del costrutto bash ${!VARIABLE}che valuta il valore della variabile il cui nome è il valore di VARIABLE; qui la variabile è la variabile speciale #che contiene il numero del parametro posizionale, quindi ${!#}è l'ultimo parametro posizionale.

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

Mi piace l'uso -udell'opzione read
Felipe Alvarez,

1

Un approccio sarebbe quello di usare read -rainvece di solo read. Assumendo i filestoCompare.txtcontenuti 2 colonne con i nomi di file in ciascuna, la read -raleggeva due colonne allo stesso tempo e li assegna in una matrice compareFile. È quindi possibile accedere a questo array in modo che l'indice 0 sia il primo file e l'indice 1 sia il 2o file ogni volta che si verifica il whileciclo.

Esempio

Di 'che ho questo file:, filestoCompare.txte contiene quanto segue:

file1 file2
file3 file4
file5 file6

Il comando per passare attraverso questo file sarebbe il seguente:

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

Se i 2 file sono effettivamente file separati come:

#list1
file1
file2
file3

#list2
file4
file5
file6

Possono essere uniti insieme al pastecomando in questo modo:

$ paste list1 list2 > list1and2

Ecco il contenuto di list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6

Ma questo non è il formato di input: gli elenchi sono in due file diversi. Potresti joinprima loro.
Gilles 'SO- smetti di essere malvagio' l'

@Gilles - So che non è il formato di input, credo di aver persino detto che "... Supponendo che filestoCompare.txt contenesse 2 colonne con i nomi dei file in ciascuno ...". Capisco la tua tesi e non sono d'accordo. Il PO non ha fornito ulteriori indicazioni su questa domanda da quando è stato pubblicato.
slm

@Gilles - cosa succede se aggiungo un esempio che mostra come utilizzare il comando pasteper unire i 2 file? Questo ti porterebbe a non votare?
slm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.