Cómo comparar Dos Arrays en Shell Script

Tengo dos archivos SQL A.sql y B.sql. Mi requisito es comparar A.sql y B.sql y necesito comprobar query que están presentes en A.sql está presente en B.sql o no, si no está allí en B.sql entonces necesitamos imprimir los contenidos que están allí en A.sql y no allí en B.sql. Así que básicamente estoy almacenando el SQL Query hasta Semicolon como una consulta y comparando tanto los arrays como la impresión del contenido.

A continuación se muestra el ejemplo

A.sql

Select * from emp;
Select * from dept;
Select * from student;
Select * from subject;

B.sql

Select * from emp;
Select * from dept;
Select * from student;

Producto exceptuado

Select * from subject;

Producto lo que estoy recibiendo

Select * from emp;

Debajo está mi guión

 i=0
 while read -rd ';' first_sql
    do
      first_array[$i]=$first_sql
      i=$((i+1))
    done < A.sql 
  
 j=0
    while read -rd ';'second_sql
    do
      second_array[$j]=$second_sql
      j=$((j+1))
    done < B.sql 

for p in "${first_array[@]}"; do
  flag=false
  for q in "${second_array[@]}"; do
    if [[ $p == $q ]]; then
      echo "$p is in first_array"
      flag=true
      break
    fi
  done
  echo $flag 
echo "$p is not in first_array"
done

Así que ahora estoy leyendo el primer archivo SQL i.e A.sql hasta Semicolon como una consulta y almacenarlo a la matriz.

 i=0
    while read -rd ';' first_sql
    do
      first_array[$i]=$first_sql
      i=$((i+1))
    done < A.sql 

Así que ahora estoy leyendo Segundo archivo SQL i.e B.sql hasta Semicolon como una consulta y almacenarlo a la matriz.

j=0
while read -rd ';'second_sql
do
  second_array[$j]=$second_sql
  j=$((j+1))
done < B.sql 

Ahora estoy comparando first_array y second_array e imprimiendo los contenidos que están presentes en primer_array y no presentes en segundo_array fuera del interior para bucle.

for p in "${first_array[@]}"; do
  flag=false
  for q in "${second_array[@]}"; do
    if [[ $p == $q ]]; then
      echo "$p is in first_array"
      flag=true
      break
    fi
  done
  echo $flag 
echo "$p is not in first_array"
done

¿Puede alguien por favor ayudar con el tema anterior y hacerme saber lo que está mal.

Nota : Mi única consulta SQL no ocupa una sola línea, será más de 4-5 líneas. Sólo por un ejemplo he usado una sola consulta de línea.

Ya que mi única consulta SQL tiene más de 4-5 líneas, por lo que estoy leyendo SQL query en mientras bucle hasta semicolon y almacenarlo en un array y luego estoy comparando esos dos arrays para imprimir el contenido sin igual.

¡Gracias por adelantado!

Pregunta hecha hace 3 años, 4 meses, 29 días - Por compilercaptain7dab


3 Respuestas:

  • Puedes hacerlo con un liner que usa comm para imprimir sólo las declaraciones que aparecen en el primer archivo, con un poco de procesamiento previo y post para dar cuenta de declaraciones multilíneas sql:

    $  comm -z -23 <(perl -0777 -pe 's/;\n/;\x{0}/g' a.sql | sort -z) \
                   <(perl -0777 -pe 's/;\n/;\x{0}/g' b.sql | sort -z) \
       | tr "\0" "\n"
    Select * from subject;
    

    (Esto supone un usuario de GNU; otras versiones de comm y sort podría no tomar el -z opción).

    Respondida el Dec 17, 2020 a las 16:09 - por pixelpioneer

    Votos positivos: 0 | Votos negativos: 0

  • El script que has proporcionado se acerca mucho a lo que necesitas hacer, pero hay un pequeño error que está causando que no obtengas la salida esperada.

    En el bucle donde estás comparando los elementos de los dos arrays, el echo que dice "$p is not in first_array" está fuera del if statement que verifica si el elemento está en la segunda matriz. Por lo tanto, siempre se imprimirá esa línea para cada elemento del primer array, incluso si el elemento está en el segundo array.

    Para corregir esto, debes mover la línea echo "$p is not in first_array" dentro del if statement, justo después del if y el fi. Aquí está la corrección en tu script:

    bash
    for p in "${first_array[@]}"; do
      flag=false
      for q in "${second_array[@]}"; do
        if [[ $p == $q ]]; then
          echo "$p is in first_array"
          flag=true
          break
        fi
      done
      if [ "$flag" = false ]; then
        echo "$p is not in first_array"
      fi
    done
    

    Esta corrección asegurará que la línea "$p is not in first_array" solo se imprima para los elementos del primer array que no se encuentran en el segundo array. ¡Espero que esta solución te ayude a obtener la salida deseada!

    Respondida el Dec 18, 2020 a las 16:08 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El código que proporcionaste parece correcto, pero hay un pequeño problema lógico en la comparación de los arrays. Aquí hay una versión modificada del código que debería funcionar correctamente:

    shell
    i=0
    while read -rd ';' first_sql
    do
      first_array[$i]=$first_sql
      i=$((i+1))
    done < A.sql
    
    j=0
    while read -rd ';' second_sql
    do
      second_array[$j]=$second_sql
      j=$((j+1))
    done < B.sql
    
    for p in "${first_array[@]}"; do
      flag=false
      for q in "${second_array[@]}"; do
        if [[ "$p" == "$q" ]]; then
          flag=true
          break
        fi
      done
      if [[ $flag == false ]]; then
        echo "$p"
      fi
    done
    

    Los cambios realizados son:

    • Se agregó un doble corchete alrededor de `$p` y `$q` en el operador de comparación `if`. Esto es necesario para comparar cadenas en Bash.
    • Se cambió la condición en el bloque `if` interior a `[[ $flag == false ]]` para verificar si la bandera es `false`. Esto producirá el `Select * from subject;` consulta esperada como resultado.

    Ahora, este código debería leer correctamente las consultas SQL de ambos archivos, compararlas y generar las consultas que están presentes en A.sql pero no en B.sql.

    Respondida el Dec 18, 2020 a las 16:15 - por Gemini

    Votos positivos: 0 | Votos negativos: 0