Recorrer las líneas de dos archivos en paralelo [cerrado]

18

El objeto del script que estoy haciendo es comparar dos series de archivos. Los nombres de los archivos se almacenan en dos archivos separados, una ruta por línea. Mi idea es tener dos while readbucles, uno para cada lista de nombres de archivo, pero ¿cómo puedo mezclar los dos bucles?

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

Necesito poder comparar archivos de dos listas diferentes al mismo tiempo a través de dos mientras leo bucles ... ¿Es esto posible?

mkrouse
fuente
¿Tiene la intención de hacer este guión como un rey de desafío? Si no es así, ya existen herramientas poderosas para comparar archivos, como diff.
lgeorget
" tipo de desafío", lo siento
lgeorget
@lgeorget que está utilizando el OP diff.
terdon
ah, archivos de dos listas. Perdón por los comentarios inútiles ...
lgeorget
Evite las publicaciones
cruzadas

Respuestas:

20

No necesitas dos bucles; solo necesita leer dos archivos en un bucle.

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
fuente
eso es mucho más menos código gracias! ¿Cómo manejo una excepción de línea vacía al mismo tiempo que para los dos bucles?
mkrouse
@mkrouse, no sé qué estabas haciendo con esa variable $ server allí antes, pero sin embargo, si pruebas una línea vacía en una variable, simplemente haces lo mismo en la otra ...
psusi
7

Método 1: usa lo que sabes

Como ya sabe cómo recorrer un archivo, puede combinar los archivos y luego procesarlos. El comando pasteune dos archivos línea por línea. Pone una pestaña entre las líneas que provienen de los dos archivos, por lo que esta solución supone que no hay pestañas en los nombres de los archivos. (Puede cambiar el separador, pero debe encontrar un carácter que no esté presente en un nombre de archivo).

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

Si desea omitir las líneas en blanco, debe hacerlo en cada archivo por separado, ya que pastepodría coincidir con una línea en blanco de un archivo con una línea no en blanco de otro archivo. Puede usar greppara filtrar las líneas no en blanco.

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

Tenga en cuenta que si los dos archivos tienen longitudes diferentes, obtendrá un espacio vacío $file2(independientemente de qué lista finalizó primero).

Método 2: recorrer dos archivos

Puede poner un comando tan complejo como desee en la condición del bucle while. Si lo coloca read file1 <&3 && read file2 <&4, el ciclo se ejecutará siempre que ambos archivos tengan una línea para leer, es decir, hasta que se agote un archivo.

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

Si desea omitir las líneas en blanco, es un poco más complicado, ya que debe omitir los dos archivos de forma independiente. La manera fácil es dividir el problema en dos partes: omita las líneas en blanco de un archivo y procese las líneas que no están en blanco. Un método para omitir las líneas en blanco es procesarlo grepcomo se indicó anteriormente. Tenga cuidado con el espacio necesario entre el <operador de redireccionamiento y el <(que inicia una suscripción de comando.

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

Otro método es escribir una función que se comporte como readpero salte las líneas en blanco. Esta función puede funcionar llamando reada un bucle. No tiene que ser una función, pero una función es el mejor enfoque, tanto para organizar su código como porque ese código debe llamarse dos veces. En la función, ${!#}es una instancia de la construcción bash ${!VARIABLE}que evalúa el valor de la variable cuyo nombre es el valor de VARIABLE; aquí la variable es la variable especial #que contiene el número de parámetro posicional, por lo que ${!#}es el último parámetro posicional.

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- deja de ser malvado'
fuente
Me gusta el uso de la -uopción de lectura
Felipe Alvarez
1

Un enfoque sería usar en read -ralugar de solo read. Suponiendo que las filestoCompare.txtcontenidas 2 columnas con los nombres de los archivos en cada uno, la read -raleerían en ambas columnas al mismo tiempo y asignarlos en una matriz, compareFile. Se puede acceder a esta matriz para que el índice 0 sea el primer archivo y el índice 1 sea el segundo archivo cada vez a través del whilebucle.

Ejemplo

Digamos que tengo este archivo: filestoCompare.txty contiene lo siguiente:

file1 file2
file3 file4
file5 file6

El comando para revisar este archivo sería el siguiente:

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

Si los 2 archivos son de hecho archivos separados como:

#list1
file1
file2
file3

#list2
file4
file5
file6

Se pueden unir con el pastecomando de la siguiente manera:

$ paste list1 list2 > list1and2

Aquí está el contenido de list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6
slm
fuente
Pero ese no es el formato de entrada: las listas están en dos archivos diferentes. Podrías joinellos primero.
Gilles 'SO- deja de ser malvado'
@Gilles - Sé que ese no es el formato de entrada, creo que incluso dije que "... Suponiendo que los archivos toCompare.txt contenían 2 columnas con los nombres de archivo en cada ...". Entiendo tu opinión y no estoy en desacuerdo. El OP no ha proporcionado más orientación sobre esta pregunta desde que la publicó.
slm
@Gilles: ¿qué pasa si agrego un ejemplo que muestra cómo usar el comando pastepara unir los 2 archivos? ¿Eso te llevaría a desestimar?
slm