2009-10-09 40 views
12

Un script python controla una aplicación externa en Linux, transfiere la entrada a través de un conducto al stdin de aplicaciones externas y lee la salida mediante un canal desde las aplicaciones externas stdout.Obligar a la salida estándar de otro programa mediante Python

El problema es que las escrituras en las tuberías se almacenan en búfer por bloque y no por línea y, por lo tanto, se producen retrasos antes de que la secuencia de comandos de control reciba datos impresos, por ejemplo, printf en la aplicación externa.

La aplicación externa no se puede modificar para agregar llamadas explícitas de fflush (0).

¿Cómo se puede utilizar el módulo pty de la biblioteca estándar de python con el módulo subprocess para lograr esto?

Respuesta

-1

Intente ejecutar el intérprete de Python con el argumento -u:

python -u myscript.py 

Esto obliga a utilizar Python para la entrada estándar sin búfer/salida estándar que pueden ayudarle.

+0

Gracias, pero el problema es la naturaleza de búfer de stdin/stdout del proceso controlado, no del script de python. – grrussel

3

No creo que sea posible. Si la aplicación de origen no vacía su búfer de salida, los datos no llegarán fuera de ese proceso hasta que el búfer se desborde y se fuerce una descarga.

Observe cómo un comando bien establecido como file tiene una opción (-n) que hace que descargue su salida explícitamente. Esto es necesario cuando se utiliza el archivo en el modo donde lee los nombres de los archivos de entrada de un conducto, e imprime el tipo detectado. Como en este modo, el programa de archivo no se cierra cuando está hecho, la salida no aparecería. Considere esto en un nivel inferior: el buffering de salida simplemente significa que al hacer write() en un buffer almacena los datos en un búfer en memoria, hasta que se llene o hasta que se encuentre un salto de línea. Luego, la parte del búfer hasta el desbordamiento o avance de línea se escribe write() n en el descriptor de archivo del nivel del sistema subyacente (que podría ser un archivo, un conducto, un zócalo, ...).

No entiendo cómo vas a convencer a ese programa para que limpie su memoria intermedia, desde el exterior.

+0

Creo que es posible (por ejemplo, http://stackoverflow.com/questions/1401002/trick-an-application- into-thinking-its-stdin-is-interactive-not-a-pipe) pero me gustaría lograr esto en el script python mismo. – grrussel

+0

@grrussel: Por lo que puedo ver, ambos tratan con entradas, en lugar de salidas. – unwind

+1

@grrussel Creo que eso solo funcionaría si la aplicación ya hace una distinción acerca de que se ejecuta de forma interactiva o en modo por lotes ... – fortran

5

Hacer esto es posible, pero la única solución que puedo pensar es bastante intrincada, no portátil, y probablemente cargada de detalles problemáticos. Puede usar LD_PRELOAD para hacer que la aplicación externa cargue una biblioteca dinámica que contiene un constructor que invoca setvbuf para desvincular stdout. Probablemente también desees ajustar setvbuf en la biblioteca para evitar que la aplicación bipeda explícitamente su propia stdout. Y querrá ajustar fwrite e printf para que se vayan en cada llamada. Escribir el .so que se precargará lo llevará fuera de python.

+1

Actualmente configuré el stdout como una tubería, lo que da como resultado el almacenamiento en bloque bloque por bloque. Si fuera capaz de establecer stdout a un pseudo terminal en su lugar, obtendría salida línea por línea. – grrussel

2

Vale la pena señalar que algunos programas solo protegen su salida cuando creen que no va a un "usuario real" (es decir, un tty). Cuando detectan que su salida está siendo leída por otro programa, almacenan en búfer.

La emulación de un tty es una de las cosas que Expect hace en la automatización de otros procesos.

Hay un pure Python implementation of Expect, pero no sé qué tan bien maneja la emulación tty.

+0

pexpect parece manejar esto bien. Creo que usa la biblioteca python pty debajo del capó. – krupan

6

Puede utilizar los PTY a resolver esto:

  • Creación de un/par maestro-esclavo Pty;
  • Conectando stdin, stdout y stderr del proceso secundario al dispositivo auxiliar pty;
  • Lectura y escritura en el pty master en el padre.
1

Esta pregunta es un poco antigua, pero creo que su problema podría resolverse ahora mediante el uso de un subproceso para llamar al stdbuf con el comando que desea ejecutar.

Cuestiones relacionadas