He explorado casi todas las preguntas similares disponibles , sin resultado.
Permítanme describir el problema en detalle:
Ejecuto algunos scripts desatendidos y estos pueden producir resultados estándar y líneas de error estándar, quiero capturarlos en su orden preciso como se muestra en un emulador de terminal y luego agregarles un prefijo como "STDERR:" y "STDOUT:".
He intentado usar tuberías e incluso un enfoque basado en epoll en ellas, sin éxito. Creo que la solución está en uso pty, aunque no soy un maestro en eso. También he echado un vistazo al código fuente del VTE de Gnome , pero eso no ha sido muy productivo.
Lo ideal sería utilizar Go en lugar de Bash para lograr esto, pero no he podido. Parece que las tuberías prohíben automáticamente mantener un orden de líneas correcto debido al almacenamiento en búfer.
¿Alguien ha podido hacer algo similar? ¿O es simplemente imposible? Creo que si un emulador de terminal puede hacerlo, entonces no es así, ¿tal vez creando un pequeño programa en C que maneje los PTY de manera diferente?
Idealmente, usaría una entrada asíncrona para leer estos 2 flujos (STDOUT y STDERR) y luego volver a imprimirlos en segundo lugar según mis necesidades, ¡pero el orden de entrada es crucial!
NOTA: Soy consciente de stderred pero no funciona para mí con los scripts de Bash y no se puede editar fácilmente para agregar un prefijo (ya que básicamente envuelve muchas llamadas de sistema).
Actualización: agregado debajo de dos aspectos esenciales
(se pueden agregar retrasos aleatorios de menos de un segundo en el script de muestra que proporcioné para probar un resultado consistente)
Actualización: la solución a esta pregunta también resolvería esta otra pregunta , como señaló @Gilles. Sin embargo, he llegado a la conclusión de que no es posible hacer lo que se pide aquí y allá. Cuando se usan 2>&1
ambas transmisiones se fusionan correctamente en el nivel pty / pipe, pero para usar las transmisiones por separado y en el orden correcto, se debe utilizar el enfoque de stderred que invoca el enganche de syscall y puede verse como sucio de muchas maneras.
Estaré ansioso por actualizar esta pregunta si alguien puede corregir la información anterior.
fuente
Respuestas:
Puede usar coprocesos. Contenedor simple que alimenta ambas salidas de un comando dado a dos
sed
instancias (una parastderr
la otrastdout
), que realizan el etiquetado.Tenga en cuenta varias cosas:
Es un encantamiento mágico para muchas personas (incluyéndome a mí), por una razón (vea la respuesta vinculada a continuación).
No hay garantía de que ocasionalmente intercambie un par de líneas, todo depende de la programación de los coprocesos. En realidad, está casi garantizado que en algún momento lo hará. Dicho esto, si mantiene el orden estrictamente igual, debe procesar los datos de ambos
stderr
ystdin
en el mismo proceso, de lo contrario, el programador del núcleo puede (y lo hará) un desastre.Si entiendo el problema correctamente, significa que necesitaría indicarle al shell que redirija ambas secuencias a un solo proceso (que se puede hacer AFAIK). El problema comienza cuando ese proceso comienza a decidir sobre qué actuar primero: tendría que sondear ambas fuentes de datos y, en algún momento, llegar al estado en el que procesaría una secuencia y los datos llegarían a ambas secuencias antes de que finalice. Y eso es exactamente donde se descompone. También significa que envolver las llamadas al sistema de salida como
stderred
es probablemente la única forma de lograr el resultado deseado (e incluso entonces podría tener un problema una vez que algo se multiprocese en un sistema multiprocesador).En cuanto a los coprocesos, asegúrese de leer la excelente respuesta de Stéphane en ¿Cómo utiliza el comando coproc en Bash? para una visión profunda.
fuente
./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected
copiando / pegando su script)bash
con/bin/sh
(no estoy seguro de por qué lo tenía allí).eval $@
es bastante buggy Úselo"$@"
si desea ejecutar sus argumentos como una línea de comando exacta: agregar una capa deeval
interpretación arroja un montón de elementos difíciles de predecir (y potencialmente maliciosos, si está pasando nombres de archivos u otro contenido que no controla como argumentos), y al no citar incluso más (divide los nombres con espacios en varias palabras, expande los globos incluso si se citaron previamente como literales, etc.).eval
cerrar los descriptores de archivo nombrados en una variable.exec {SEDo[1]}>&-
funcionará tal cual (sí, la falta de un$
antes del{
deliberado).Método 1. Usando descriptores de archivo y awk
¿Qué pasa con algo como esto usando las soluciones de este SO Q&A titulado: ¿Hay una utilidad Unix para anteponer marcas de tiempo a las líneas de texto? y este SO Q&A titulado: canalizar STDOUT y STDERR a dos procesos diferentes en el script de shell .
El enfoque
Paso 1, creamos 2 funciones en Bash que realizarán el mensaje de marca de tiempo cuando se llame:
Paso 2 usaría las funciones anteriores para obtener el mensaje deseado:
Ejemplo
Aquí he inventado un ejemplo que escribirá
a
en STDOUT, duerme durante 10 segundos y luego escribe la salida en STDERR. Cuando ponemos esta secuencia de comandos en nuestra construcción anterior, recibimos mensajes como usted especificó.Método 2. Usando anotar-salida
Hay una herramienta llamada
annotate-output
que es parte deldevscripts
paquete que hará lo que quieras. Su única restricción es que debe ejecutar los scripts por usted.Ejemplo
Si ponemos nuestra secuencia de comandos de ejemplo anterior en un script llamado
mycmds.bash
así:Entonces podemos ejecutarlo así:
El formato de salida se puede controlar para la parte de marca de tiempo, pero no más allá de eso. Pero es una salida similar a la que está buscando, por lo que puede cumplir con los requisitos.
fuente
stderred
usted no puede determinar fácilmente los límites de las líneas (intentarlo sería hackear). Quería ver si alguien podría ayudarme con este problema, pero aparentemente todos quieren renunciar a la única restricción ( orden ) que es la base de la pregunta