Fuerce el enjuague de salida a un archivo mientras el script bash aún se está ejecutando

Tengo un pequeño script, que es llamado diariamente por crontab usando el siguiente comando:

/homedir/MyScript &> some_log.log 

El problema con este método es que some_log.log solo se crea después de que MyScript finaliza. Me gustaría descargar la salida del progtwig en el archivo mientras se está ejecutando para poder hacer cosas como

 tail -f some_log.log 

y haga un seguimiento del progreso, etc.

bash en sí mismo nunca escribirá ningún resultado en su archivo de registro. En cambio, los comandos que invoca como parte de la secuencia de comandos escribirán cada salida individualmente y ruborizar cada vez que lo deseen. Entonces su pregunta es realmente cómo forzar los comandos dentro del script bash para que se vayan, y eso depende de lo que sean.

Encontré una solución a esto aquí . Usando el ejemplo de OP básicamente se ejecuta

stdbuf -oL /homedir/MyScript &> some_log.log

y luego el buffer se enjuaga después de cada línea de salida. A menudo combino esto con nohup para ejecutar trabajos largos en una máquina remota.

stdbuf -oL nohup /homedir/MyScript &> some_log.log

De esta forma, su proceso no se cancela cuando cierra la sesión.

script -c -f OUTPUT.txt

La clave es -f. Cita de la secuencia de comandos del hombre:

  -f, --flush Flush output after each write. This is nice for telecooperation: one person does `mkfifo foo; script -f foo', and another can supervise real-time what is being done using `cat foo'. 

Ejecutar en segundo plano:

 nohup script -c  -f OUTPUT.txt 

Puede usar tee para escribir en el archivo sin necesidad de enjuagar.

 /homedir/MyScript 2>&1 | tee some_log.log > /dev/null 

Esto no es una función de bash , ya que todo lo que hace el shell es abrir el archivo en cuestión y luego pasar el descriptor de archivo como salida estándar del script. Lo que debe hacer es asegurarse de que la salida se borre de su secuencia de comandos con más frecuencia que la que tiene actualmente.

En Perl, por ejemplo, esto podría lograrse estableciendo:

 $| = 1; 

Ver perlvar para más información sobre esto.

Ayudaría esto?

 tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

Esto mostrará de forma inmediata entradas únicas de access.log
http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html

Como acabo de descubrir aquí, el problema es que tienes que esperar para que los progtwigs que ejecutas desde tu script terminen sus trabajos.
Si en su secuencia de comandos ejecuta el progtwig en segundo plano , puede intentar algo más.

En general, una llamada para sync antes de salir permite eliminar los búferes del sistema de archivos y puede ayudar un poco.

Si en el script inicia algunos progtwigs en segundo plano ( & ), puede esperar a que terminen antes de salir del script. Para tener una idea acerca de cómo puede funcionar, puede ver a continuación

 #!/bin/bash #... some stuffs ... program_1 & # here you start a program 1 in background PID_PROGRAM_1=${!} # here you remember its PID #... some other stuffs ... program_2 & # here you start a program 2 in background wait ${!} # You wait it finish not really useful here #... some other stuffs ... daemon_1 & # We will not wait it will finish program_3 & # here you start a program 1 in background PID_PROGRAM_3=${!} # here you remember its PID #... last other stuffs ... sync wait $PID_PROGRAM_1 wait $PID_PROGRAM_3 # program 2 is just ended # ... 

Como la wait funciona tanto con trabajos como con números PID , una solución lenta debe colocarse al final del script.

 for job in `jobs -p` do wait $job done 

Más difícil es la situación si ejecuta algo que ejecuta algo más en segundo plano porque tiene que buscar y esperar (si es el caso) el final de todo el proceso secundario : por ejemplo, si ejecuta un daemon, probablemente no sea el caso esperar a que termine :-).

Nota:

  • espera $ {!} significa “espera hasta que se complete el último proceso en segundo plano” donde $! es el PID del último proceso en segundo plano. Entonces, poner wait ${!} Justo después de program_2 & es equivalente a ejecutar directamente program_2 sin enviarlo en segundo plano con &

  • De la ayuda de wait :

     Syntax wait [n ...] Key n A process ID or a job specification 

El /homedir/MyScript de salida depende de cómo se implemente su progtwig /homedir/MyScript . Si encuentra que la salida está siendo amortiguada, debe forzarla en su implementación. Por ejemplo, use sys.stdout.flush () si es un progtwig python o use fflush (stdout) si se trata de un progtwig C.

¡Gracias @user3258569 , el script es quizás lo único que funciona en busybox !

Sin embargo, el caparazón estaba helado para mí después de eso. Buscando la causa, encontré estas grandes advertencias rojas “no usar en un caparazón no interactivo” en la página del manual de scripts :

script está diseñado principalmente para sesiones de terminal interactivas. Cuando stdin no es un terminal (por ejemplo: echo foo | script ), entonces la sesión puede bloquearse, porque el shell interactivo dentro de la sesión de script pierde EOF y el script no tiene idea de cuándo cerrar la sesión. Vea la sección NOTES para más información.

Cierto. script -c "make_hay" -f /dev/null | grep "needle" script -c "make_hay" -f /dev/null | grep "needle" estaba congelando el caparazón para mí.

En contra de la advertencia, pensé que echo "make_hay" | script echo "make_hay" | script pasará un EOF, así que lo intenté

 echo "make_hay; exit" | script -f /dev/null | grep 'needle' 

¡Y funcionó!

Tenga en cuenta las advertencias en la página de manual. Esto puede no funcionar para ti.

No sé si funcionaría, pero ¿qué pasa con la sync llamadas?

Tuve este problema con un proceso en segundo plano en Mac OS X usando StartupItems . Así es como lo resuelvo:

Si hago sudo ps aux , puedo ver que mytool se lanzó.

Descubrí que (debido al almacenamiento en búfer) cuando Mac OS X se apaga, mytool nunca transfiere la salida al comando sed . Sin embargo, si ejecuto sudo killall mytool , mytool transfiere la salida al comando sed . Por lo tanto, agregué un caso de stop a StartupItems que se ejecuta cuando Mac OS X se apaga:

 start) if [ -x /sw/sbin/mytool ]; then # run the daemon ConsoleMessage "Starting mytool" (mytool | sed .... >> myfile.txt) & fi ;; stop) ConsoleMessage "Killing mytool" killall mytool ;; 

me gusta o no, así es como funciona la redirección.

En su caso, el resultado (es decir, el guión finalizado) de su guión redirigido a ese archivo.

Lo que quiere hacer es agregar esas redirecciones en su script.