Tengo un script que llama a dos comandos:
long_running_command | print_progress
Las long_running_command
impresiones de un progreso, pero estoy contento con él. Estoy usando print_progress
para hacerlo más agradable (es decir, imprimo el progreso en una sola línea).
El problema: la conexión de una tubería a stdout también activa un búfer 4K, para que el programa de impresión agradable no obtenga nada ... nada ... nada ... mucho ... :)
¿Cómo puedo deshabilitar el búfer 4K para long_running_command
(no, no tengo la fuente)?
Respuestas:
Puede usar el
unbuffer
comando (que viene como parte delexpect
paquete), p. Ej.unbuffer
se conecta along_running_command
través de un pseudoterminal (pty), lo que hace que el sistema lo trate como un proceso interactivo, por lo tanto, no utiliza el almacenamiento en búfer de 4 kiB en la tubería que es la causa probable del retraso.Para canalizaciones más largas, es posible que deba quitar el búfer de cada comando (excepto el último), por ejemplo
fuente
expect_unbuffer
y está en elexpect-dev
paquete, no en elexpect
paqueteexpect-dev
proporciona ambosunbuffer
yexpect_unbuffer
(el primero es un enlace simbólico al segundo). Los enlaces están disponibles desdeexpect 5.44.1.14-1
(2009).Otra forma de pelar este gato es usar el
stdbuf
programa, que es parte de GNU Coreutils (FreeBSD también tiene el suyo propio).Esto desactiva el almacenamiento en búfer por completo para entrada, salida y error. Para algunas aplicaciones, el almacenamiento en línea puede ser más adecuado por razones de rendimiento:
Tenga en cuenta que solo funciona para el
stdio
almacenamiento en búfer (printf()
,fputs()
...) para aplicaciones vinculadas dinámicamente, y solo si esa aplicación no ajusta el almacenamiento en búfer de sus flujos estándar por sí mismo, aunque eso debería cubrir la mayoría de las aplicaciones.fuente
sudo stdbuff … command
trabajos aunquestdbuff … sudo command
no lo hice.stdbuf
no funcionatee
porquetee
sobrescribe los valores predeterminados establecidos porstdbuf
. Consulte la página del manual destdbuf
.stdbuf
usa unLD_PRELOAD
mecanismo para insertar su propia biblioteca cargada dinámicamentelibstdbuf.so
. Esto significa que no funcionará con estos tipos de ejecutables: con setuid o capacidades de archivo establecidas, estáticamente vinculadas, sin usar libc estándar. En estos casos, es mejor usar las soluciones conunbuffer
/script
/socat
. Ver también stdbuf con setuid / capacidades .Otra forma más de activar el modo de salida de almacenamiento en línea para el
long_running_command
es usar elscript
comando que ejecuta sulong_running_command
en un pseudo terminal (pty).fuente
script
es un comando tan antiguo, debería estar disponible en todas las plataformas tipo Unix.-q
en Linux:script -q -c 'long_running_command' /dev/null | print_progress
stdin
, lo que hace que sea imposible ejecutarlolong_running_command
en segundo plano, al menos cuando se inicia desde un terminal interactivo. Para solucionarlo, pude redirigir stdin desde/dev/null
, ya que milong_running_command
no usastdin
.Para
grep
,sed
yawk
puede forzar la salida para que sea almacenada en línea. Puedes usar:Fuerce la salida para que sea almacenada en línea. De manera predeterminada, la salida está protegida en línea cuando la salida estándar es un terminal y el bloque está protegido de otra manera.
Hacer que la línea de salida sea amortiguada.
Consulte esta página para obtener más información: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
fuente
Si es un problema con el libc modificando su almacenamiento en búfer / vaciado cuando la salida no va a una terminal, debe intentar socat . Puede crear una secuencia bidireccional entre casi cualquier tipo de mecanismo de E / S. Uno de ellos es un programa bifurcado que habla a un pseudo tty.
Lo que hace es
Si esto le da el mismo resultado que
long_running_command
, entonces puede continuar con una tubería.Editar: Wow ¡No vi la respuesta de unbuffer! Bueno, socat es una gran herramienta de todos modos, así que podría dejar esta respuesta
fuente
socat -u exec:long_running_command,pty,end-close -
aquíPuedes usar
El problema es que libc hará un buffer de línea cuando stdout a la pantalla, y un buffer completo cuando stdout a un archivo. Pero sin buffer para stderr.
No creo que sea el problema con el buffer de tubería, se trata de la política de buffer de libc.
fuente
zsh
(de donde|&
viene adaptado de csh) ybash
, cuando lo hacescmd1 >&2 |& cmd2
, ambos fd 1 y 2 están conectados a la salida estándar externa. Por lo tanto, funciona para evitar el almacenamiento en búfer cuando esa salida externa es un terminal, pero solo porque la salida no pasa por la tubería (por lo queprint_progress
no imprime nada). Entonces es lo mismo quelong_running_command & print_progress
(excepto que print_progress stdin es una tubería que no tiene escritor). Puede verificar enls -l /proc/self/fd >&2 |& cat
comparación conls -l /proc/self/fd |& cat
.|&
es la abreviatura de2>&1 |
, literalmente. Asícmd1 |& cmd2
escmd1 1>&2 2>&1 | cmd2
. Entonces, tanto fd 1 como 2 terminan conectados al stderr original, y no queda nada escrito en la tubería. (s/outer stdout/outer stderr/g
en mi comentario anterior)Solía ser el caso, y probablemente sigue siendo el caso, que cuando la salida estándar se escribe en un terminal, se almacena la línea de forma predeterminada, cuando se escribe una nueva línea, la línea se escribe en el terminal. Cuando la salida estándar se envía a una tubería, está completamente protegida, por lo que los datos solo se envían al siguiente proceso en la tubería cuando se llena la memoria intermedia de E / S estándar.
Esa es la fuente del problema. No estoy seguro de si hay mucho que pueda hacer para solucionarlo sin modificar el programa que se escribe en la tubería. Puede usar la
setvbuf()
función con la_IOLBF
bandera para poner incondicionalmentestdout
en modo de línea de búfer. Pero no veo una manera fácil de aplicar eso en un programa. O el programa puede hacerlofflush()
en los puntos apropiados (después de cada línea de salida), pero se aplica el mismo comentario.Supongo que si reemplaza la tubería con un pseudo-terminal, la biblioteca de E / S estándar pensaría que la salida es un terminal (porque es un tipo de terminal) y alinearía el búfer automáticamente. Sin embargo, esa es una forma compleja de lidiar con las cosas.
fuente
Sé que esta es una pregunta antigua y que ya tenía muchas respuestas, pero si desea evitar el problema del búfer, intente algo como:
Esto generará en tiempo real los registros y también los guardará en el
logs.txt
archivo y el búfer ya no afectará eltail -f
comando.fuente
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
No creo que el problema sea con la tubería. Parece que su proceso de larga ejecución no está volcando su propio búfer con la frecuencia suficiente. Cambiar el tamaño del búfer de la tubería sería un truco para evitarlo, pero no creo que sea posible sin reconstruir el kernel, algo que no querría hacer como un pirateo, ya que probablemente afecte a muchos otros procesos.
fuente
De acuerdo con esta publicación aquí , podría intentar reducir el límite de la tubería a un solo bloque de 512 bytes. Ciertamente no desactivará el almacenamiento en búfer, pero bueno, 512 bytes es mucho menor que 4K: 3
fuente
De manera similar a la respuesta de Chad , puedes escribir un pequeño guión como este:
Luego use este
scriptee
comando como reemplazo detee
.Por desgracia, parece que no puedo obtener una versión como esa que funcione perfectamente en Linux, por lo que parece estar limitado a los unixes de estilo BSD.
En Linux, esto está cerca, pero no recibe su solicitud cuando finaliza (hasta que presiona enter, etc.) ...
fuente
script
emula un terminal, así que sí, creo que desactiva el almacenamiento en búfer. También repite cada carácter que se le envía, por lo quecat
se envía/dev/null
en el ejemplo. En lo que respecta al programa que se ejecuta dentroscript
, se trata de una sesión interactiva. Creo que es similar aexpect
este respecto, peroscript
probablemente sea parte de su sistema base.tee
es para enviar una copia de la transmisión a un archivo. ¿Dónde se especifica el archivoscriptee
?cat
comando contee myfile.txt
, y debería obtener el efecto que desea.Encontré esta solución inteligente:
(echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable
Esto hace el truco.
cat
leerá entradas adicionales (hasta EOF) y las pasará a la tubería después de queecho
haya puesto sus argumentos en la secuencia de entrada deshell_executable
.fuente
cat
no ve la salida deecho
; solo ejecuta dos comandos en una subshell y la salida de ambos se envía a la tubería. El segundo comando en el subshell ('cat') se lee desde el stdin principal / externo, por eso funciona.De acuerdo con esto, el tamaño del búfer de tubería parece estar configurado en el núcleo y requeriría que vuelva a compilar su núcleo para modificarlo.
fuente