Estoy ejecutando un punto de referencia ensimulador gem5, que sigue imprimiendo la salida al terminal mientras se ejecuta. Ya almacené una ejecución de muestra del mismo punto de referencia en unArchivo de texto.
Ahora quiero comparar el flujo de salida que se imprime en la consola con el archivo de texto de la ejecución dorada anterior. Si hay una diferencia con la salida en comparación con el archivo de texto, la simulación debe finalizar automáticamente.
El punto de referencia requiere mucho tiempo para ejecutarse. Solo me interesa el primer error en la ejecución actual, para poder ahorrar tiempo de espera hasta que se complete la ejecución para comparar ambas salidas.
Respuesta1
No pude resistirme a pensar un poco más en cómo encontrar una forma adecuada de comparar la salida de unproceso en ejecución(en la terminal) contra un archivo de "ejecución dorada", como lo mencionas.
Cómo capturar el resultado del proceso en ejecución
Usé el script
comando con la -f
opción. Esto escribe el contenido actual (textual) del terminal en un archivo; la -f
opción es actualizar el archivo de salida en cada evento de escritura en el terminal. El comando de secuencia de comandos está diseñado para mantener un registro de todo lo que sucede en una ventana de terminal.
El siguiente script importa esta salida periódicamente.
Qué hace este script
Si ejecuta el script en una ventana de terminal, abre una segunda ventana de terminal, iniciada con el script -f
comando. En esta (segunda) ventana de terminal, debe ejecutar su comando para iniciar el proceso de evaluación comparativa. Si bien este proceso de referencia produce sus resultados, estos resultados se comparan periódicamente (cada 2 segundos) con su "ejecución dorada". Si se produjo una diferencia, la salida diferente se muestra en la terminal "principal" (primera) y el script finaliza. Aparece una línea, en el formato:
error: ('Solutions: 13.811084', 'Solutions: 13.811084 aap noot mies')
explanation:
error: (<golden_run_result>, <current_differing_output>)
Después de este resultado, puede cerrar de forma segura la segunda ventana y ejecutar sus pruebas.
Cómo utilizar
Copie el siguiente script en un archivo vacío.
Cuando observa su archivo de "ejecución dorada", la primera sección (antes de que comience la prueba real) es irrelevante y puede diferir en diferentes sistemas. Por lo tanto, es necesario definir la línea donde comienza la salida real. En tu caso lo puse en:first_line = "**** REAL SIMULATION ****"
cámbielo si es necesario.
- Establezca la ruta a su archivo "golden run".
Guarde el script como
compare.py
, ejecútelo con el comando:python3 /path/to/compare.py
`
- se abre una segunda ventana que dice
Script started, the file is named </path/to/file>
- En esta segunda ventana, ejecute su prueba comparativa, el primer resultado diferente aparece en la primera ventana:
como lo probé
Creé un pequeño programa que imprime las líneas de una versión editada de tu ejecución dorada, una por una. Hice que el script lo comparara con el archivo original de "ejecución dorada".
La secuencia de comandos:
#!/usr/bin/env python3
import subprocess
import os
import time
home = os.environ["HOME"]
# files / first_line; edit if necessaary
golden_run = "/home/jacob/Bureaublad/log_example"
first_line = "**** REAL SIMULATION ****"
# don't change anything below
typescript_outputfile = home+"/"+"scriptlog.txt"
# commands
startup_command = "gnome-terminal -x script -f "+typescript_outputfile
clean_textcommand = "col -bp <"+typescript_outputfile+" | less -R"
# remove old outputfile
try:
os.remove(typescript_outputfile)
except Exception:
pass
# initiate typescript
subprocess.Popen(["/bin/bash", "-c", startup_command])
time.sleep(1)
# read golden run
with open(golden_run) as src:
original = src.read()
orig_section = original[original.find(first_line):]
# read last output of current results so far
def get_last():
read = subprocess.check_output(["/bin/bash", "-c", clean_textcommand]).decode("utf-8")
if not first_line+"\n" in read:
return "Waiting for first line"
else:
return read[read.find(first_line):]
with open(typescript_outputfile, "wt") as clear:
clear.write("\n")
# loop
while True:
current = get_last()
if current == "\n":
pass
else:
if not current in orig_section and current != "Waiting for first line":
orig = orig_section.split("\n")
breakpoint = current.split("\n")
diff = [(orig[i], breakpoint[i]) for i in range(len(breakpoint)) \
if not orig[i] == breakpoint[i]]
print("error: "+str(diff[0]))
break
else:
pass
time.sleep(5)
Respuesta2
Puedes usar diff
util.
Supongamos que tienessulima dorada, yotroque cambié.
No tengo tu programa ejecutándose, así que escribí esta simulación:
#!/bin/bash
while read -r line; do
echo "$line";
sleep 1;
done < bad_file
se lee desdeotrofile (bad_file) y genera línea por línea cada segundo.
Ahora ejecute este script y redirija su salida al log
archivo.
$ simulate > log &
También escribí un script de verificación:
#!/bin/bash
helper(){
echo "This script takes two file pathes as arguments."
echo "$0 path/to/file1 path/to/file2"
}
validate_input(){
if [[ $# != 2 ]]; then
helper
exit 1
fi
if [[ ! -f "$1" ]]; then
echo "$1" file is not exist.
helper
exit 1
fi
if [[ ! -f "$2" ]]; then
echo "$2" file is not exist.
helper
exit 1
fi
}
diff_files(){
# As input takes two file and check
# difference between files. Only checks
# number of lines you have right now in
# your $2 file, and compare it with exactly
# the same number of lines in $1
diff -q -a -w <(tail -n+"$ULINES" $1 | head -n "$CURR_LINE") <(tail -n+"$ULINES" $2 | head -n "$CURR_LINE")
}
get_curr_lines(){
# count of lines currenly have minus ULINES
echo "$[$(cat $1 | wc -l) - $ULINES]"
}
print_diff_lines(){
diff -a -w --unchanged-line-format="" --new-line-format=":%dn: %L" "$1" "$2" | grep -o ":[0-9]*:" | tr -d ":"
}
ULINES=15 # count of first unused lines. How many first lines to ignore
validate_input "$1" "$2"
CURR_LINE=$(get_curr_lines "$2") # count of lines currenly have minus ULINES
if [[ $CURR_LINE < 0 ]];then
exit 0
fi
IS_DIFF=$(diff_files "$1" "$2")
if [[ -z "$IS_DIFF" ]];then
echo "Do nothing if they are the same"
else
echo "Do something if files already different"
echo "Line number: " `print_diff_lines "$1" "$2"`
fi
No olvides hacerlo ejecutable chmod +x checker.sh
.
Este script requiere dos argumentos. El primer argumento es la ruta a su archivo dorado, el segundo argumento es la ruta a su archivo de registro.
$ ./checker.sh path_to_golden path_to_log
Este verificador cuenta el número de líneas que tiene ahora en su log
archivo y lo compara con exactamente el mismo número de líneas en golden_file
.
Ejecuta el verificador cada segundo y ejecuta el comando matar si es necesario
Si lo desea, puede escribir la función bash para que se ejecute checker.sh
cada segundo:
$ chk_every() { while true; do ./checker.sh $1 $2; sleep 1; done; }
Parte de la respuesta anterior sobre diff.
Puedes compararlos línea por línea como un archivo de texto.
Deman diff
NAME
diff - compare files line by line
-a, --text
treat all files as text
-q, --brief
report only when files differ
-y, --side-by-side
output in two columns
Si comparamos nuestros archivos:
$ diff -a <(tail -n+15 file1) <(tail -n+15 file2)
Veremos esta salida:
2905c2905
< Solutions: 0.686669
---
> Solutions: 0.686670
2959c2959
< Solutions: 0.279124
---
> Solutions: 0.279125
3030c3030
< Solutions: 0.539016
---
> Solutions: 0.539017
3068c3068
< Solutions: 0.308278
---
> Solutions: 0.308279
Muestra la línea que difiere.
Y aquí está el comando final, asumiendo que no deseas verificar las primeras 15 líneas:
$ diff -y -a <(tail -n+15 file1) <(tail -n+15 file2)
Le mostrará todas las diferencias en dos columnas. Si sólo quieres saber si hay alguna diferencia, usa esto:
$ diff -q -a <(tail -n+15 file1) <(tail -n+15 file2)
No imprimirá nada si los archivos son iguales.
Respuesta3
No tengo idea de cuán complicados son tus datos de entrada, pero podrías usar algo como awk
leer cada línea a medida que ingresa y compararla con un valor conocido.
$ for i in 1 2 3 4 5; do echo $i; sleep 1; done | \
awk '{print "Out:", $0; fflush(); if ($1==2) exit(0)}'
Out: 1
Out: 2
En este caso, estoy ingresando un flujo de números con retardo de tiempo y awk
lo estoy ejecutando hasta que la primera variable en la entrada (lasolovariable aquí) es igual a 2, luego sale y al hacerlo detiene la corriente.