statement filename else bash process-substitution

filename - Sustitución y sincronización del proceso Bash.



string contains bash (3)

(Posiblemente relacionado con ¿Algunos programas no aceptan la sustitución de procesos para los archivos de entrada? )

En algunos scripts de prueba de unidad de Bash, estoy usando el siguiente truco para registrar y mostrar stdout y stderr de un comando:

command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)

Este proceso produce algo de salida en stdout, por lo que el archivo $stdoutF obtiene algunos datos. Luego ejecuto otro comando que no genera ningún dato:

diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)

Sin embargo, no parece que este proceso siempre termine con éxito antes de que se ejecute la prueba de vacío (utilizando shunit-ng ):

assertNull ''Unexpected output to stdout'' "$(<"$stdoutF")"

En una prueba de ejecución 100 esto falló 25 veces.

¿Debería ser suficiente llamar a sync antes de probar que el archivo esté vacío?

sync assertNull ''Unexpected output to stdout'' "$(<"$stdoutF")"

... y / o debería funcionar forzando la secuencia de los comandos:

diff -r "$source" "$target" / > >(tee "${stdoutF}"; assertNull ''Unexpected output to stdout'' "$(<"$stdoutF")") 2> >(tee "${stderrF}" >&2)

... y / o ¿es posible assertNull alguna manera para assertNull directamente en lugar de un archivo?

Actualización : la sync no es la respuesta. Consulte la respuesta de Gilles a continuación.

Actualización 2 : discusión adicional para guardar stdout, stderr y stdout + stderr sincrónicamente . Gracias por las respuestas!


A veces pongo un guardia:

: > >(sleep 1; echo a; touch guard) / && while true; do [ -f "guard" ] && { rm guard; break; } sleep 0.2 done


En bash, un comando de sustitución de proceso foo > >(bar) finaliza tan pronto como finaliza foo . (Esto no se discute en la documentación). Puede verificar esto con

: > >(sleep 1; echo a)

Este comando regresa inmediatamente, luego imprime a asíncrono un segundo después.

En su caso, el comando tee toma solo un poco de tiempo para finalizar una vez que se completa el command . Agregar sync tee dio suficiente tiempo para completar, pero esto no elimina la condición de la carrera, más que agregar un sleep , solo hace que la carrera sea más improbable que se manifieste.

De manera más general, la sync no tiene ningún efecto observable internamente: solo hace una diferencia si desea acceder al dispositivo donde sus sistemas de archivos se almacenan en una instancia de sistema operativo diferente. En términos más claros, si su sistema pierde energía, solo los datos escritos antes de la última sync estarán disponibles después de reiniciar.

En cuanto a eliminar la condición de carrera, aquí hay algunos de los posibles enfoques:

  • Sincronizar explícitamente todos los procesos sustituidos.

    mkfifo sync.pipe command > >(tee -- "$stdoutF"; echo >sync.pipe) 2> >(tee -- "$stderrF"; echo >sync.pipe) read line < sync.pipe; read line < sync.pipe

  • Use un nombre de archivo temporal diferente para cada comando en lugar de reutilizar $stdoutF y $stderrF , e imponer que el archivo temporal siempre se crea nuevamente.

  • Renunciar a la sustitución de procesos y utilizar tuberías en su lugar.

    { { command | tee -- "$stdoutF" 1>&3; } 2>&1 / | tee -- "$stderrF" 1>&2; } 3>&1

    Si necesita el estado de retorno del comando, bash lo pone en ${PIPESTATUS[0]} .

    { { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 / | tee -- "$stderrF" 1>&2; } 3>&1 if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi


Inserta un sleep 5 o lo que sea en lugar de sync para responder a tu última pregunta