¿Cómo puedo enviar el stdout de un proceso de múltiples procesos utilizando (preferiblemente sin nombre) tuberías de Unix (o Windows)?

StackOverflow https://stackoverflow.com/questions/60942

Pregunta

Me gustaría redirigir la salida estándar (stdout) de procesos proc1 dos procesos proc2 y proc3:

         proc2 -> stdout
       /
 proc1
       \ 
         proc3 -> stdout

He intentado

 proc1 | (proc2 & proc3)

pero no parece funcionar, es decir,

 echo 123 | (tr 1 a & tr 1 b)

escribe

 b23

a la salida estándar (stdout) en lugar de

 a23
 b23
¿Fue útil?

Solución

Nota del Editor:
- >(…) es un proceso de sustitución de que es un no estándar de la shell característica de algunos Compatible con POSIX conchas: bash, ksh, zsh.
- Esta respuesta accidentalmente envía la salida del proceso de sustitución de la salida a través de la canalización demasiado: echo 123 | tee >(tr 1 a) | tr 1 b.
- Salida desde el proceso de sustituciones será impredecible intercalada, y, excepto en zsh, en la línea , se puede terminar antes de los comandos dentro de >(…) hacer.

En unix (o mac), el uso de la tee comando:

$ echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null
b23
a23

Normalmente puede utilizar tee para redirigir la salida a varios archivos, pero el uso de >(...) puede redirigir a otro proceso.Así que, en general,

$ proc1 | tee >(proc2) ... >(procN-1) >(procN) >/dev/null

hace lo que usted desea.

Bajo windows, no creo que el built-in shell tiene un equivalente.Microsoft Windows PowerShell tiene un tee comando sin embargo.

Otros consejos

Como el dF, dijo, bash permite el uso de la >(…) construir la ejecución de un comando en lugar de un nombre de archivo.(También existe la <(…) construir para sustituir la salida de otro comando en lugar de un nombre de archivo, pero eso es irrelevante ahora, yo lo menciono sólo para la integridad).

Si usted no tiene bash, o que se ejecutan en un sistema con una versión anterior de bash, se puede hacer manualmente lo que bash no, haciendo uso de FIFO archivos.

La forma genérica para lograr lo que quiere, es:

  • decidir cómo muchos de los procesos que debe recibir la salida de su comando, y crear tantas FIFOs, preferiblemente en un mundial de la carpeta temporal:
    subprocesses="a b c d"
    mypid=$$
    for i in $subprocesses # this way we are compatible with all sh-derived shells  
    do
        mkfifo /tmp/pipe.$mypid.$i
    done
  • inicio todos sus subprocesos en espera de la entrada de los FIFOs:
    for i in $subprocesses
    do
        tr 1 $i </tmp/pipe.$mypid.$i & # background!
    done
  • ejecutar el comando tee a las FIFOs:
    proc1 | tee $(for i in $subprocesses; do echo /tmp/pipe.$mypid.$i; done)
  • finalmente, eliminar las FIFOs:
    for i in $subprocesses; do rm /tmp/pipe.$mypid.$i; done

NOTA:por razones de compatibilidad, yo haría el $(…) con acentos graves, pero yo no podía hacerlo escribiendo esta respuesta (backquote es utilizado en).Normalmente, la $(…) tiene la edad suficiente para trabajar incluso en las viejas versiones de ksh, pero si no es así, incluya el parte de acentos graves.

Unix (bash, ksh, zsh)

dF.'s respuesta contiene la la semilla de una respuesta basada en la tee y salida proceso de sustituciones
(>(...)) que puede o no puede de trabajo, en función de sus necesidades:

Tenga en cuenta que el proceso de sustituciones son un no estándar característica que (en su mayoría) POSIX-características-sólo conchas como dash (que actúa como /bin/sh en Ubuntu, por ejemplo), hacer no apoyo.Secuencias de comandos de Shell de orientación /bin/sh debe no confiar en ellos.

echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null

El trampas de este enfoque son:

  • impredecible, asincrónica de salida de comportamiento:los flujos de salida de los comandos dentro del proceso de salida sustituciones >(...) entrelazan de maneras impredecibles.

  • En bash y ksh (en contraposición a la zsh - pero vea excepción, más adelante):

    • la salida puede llegar después de el comando ha terminado.
    • los comandos posteriores se puede comenzar a ejecutar antes de los comandos en el proceso de sustituciones se han terminado - bash y ksh ¿ no espere a que el proceso de salida de la sustitución de nuevos procesos para terminar, al menos por defecto.
    • jmb lo pone bien en un comentario en el dF.'s respuesta:

ser conscientes de que los comandos que se inició en el interior de >(...) son disociados de la shell original, y no se puede determinar fácilmente cuando se termine;el tee va a terminar después de escribir todo, pero el sustituido procesos se siguen consumiendo los datos de varios buffers en el kernel y e/S de archivos, además de lo que se toma el tiempo por su manejo interno de datos.Usted puede encontrar las condiciones de carrera si su carcasa exterior, a continuación, pasa a depender de todo lo producido por los sub-procesos.

  • zsh es la única shell que ¿ por defecto, espere a que los procesos se ejecutan en el proceso de salida de sustituciones para terminar, excepto si es stderr que se le redirige a una (2> >(...)).

  • ksh (al menos a partir de la versión 93u+) permite el uso de un argumento-menos wait a esperar el resultado del proceso de sustitución generado procesos de acabado.
    Tenga en cuenta que en una sesión interactiva que podría resultar en la espera de cualquier pendiente los trabajos en segundo plano también, sin embargo.

  • bash v4.4+ espere a que el más recientemente iniciado el proceso de salida de sustitución con wait $!, pero el argumento-menos wait ¿ no de trabajo, haciendo de este inadecuados para un comando con varios el proceso de salida de sustituciones.

  • Sin embargo, bash y ksh puede ser forzado a esperar por medio de tuberías y el comando para | cat, pero ten en cuenta que esto hace que el comando se ejecute en un subshell. Advertencias:

    • ksh (como de ksh 93u+) no admite el envío de stderr a un proceso de salida de sustitución (2> >(...));un intento de este tipo es ignorado.

    • Mientras zsh es (recomendables) síncrono por defecto con el (mucho más común) stdout el proceso de salida sustituciones, incluso el | cat la técnica no puede hacer sincrónico con stderr el proceso de salida sustituciones (2> >(...)).

  • Sin embargo, incluso si usted se asegure de de ejecución sincrónico, el problema de la de forma impredecible intercalado de salida sigue siendo.

El siguiente comando, cuando se ejecuta en bash o ksh, ilustra los comportamientos problemáticos (puede que tenga que ejecutar varias veces para ver ambos los síntomas):El AFTER normalmente impresión antes de la salida de la salida de sustituciones, y la salida de este último pueden ser intercalados de forma impredecible.

printf 'line %s\n' {1..30} | tee >(cat -n) >(cat -n) >/dev/null; echo AFTER

En breve:

  • Garantizar un particular por la salida del comando de la secuencia:

    • Ni bash ni ksh ni zsh el apoyo que.
  • De ejecución sincrónico:

    • Factible, excepto con stderrprocedente de la salida del proceso de sustituciones:
      • En zsh, son invariablemente asincrónica.
      • En ksh, que no funcionan en todos los.

Si usted puede vivir con estas limitaciones, el uso de la salida del proceso de sustituciones es una opción viable (por ejemplo, si ellos escriben para separar los archivos de salida).


Tenga en cuenta que tzot mucho más engorroso, pero potencialmente compatibles con POSIX solución también exhibe impredecible comportamiento de salida de;sin embargo, mediante el uso de wait usted puede asegurarse de que los comandos posteriores no comenzar a ejecutar hasta que todos los procesos en segundo plano de haber terminado.
Véase la parte inferior para de una forma más robusta, sincrónico, serializada de salida de la aplicación.


La única sencillo bash solución con el previsible comportamiento de salida de es la siguiente, que, sin embargo, es excesivamente lento con grandes conjuntos de entrada, porque shell bucles son inherentemente lento.
También tenga en cuenta que este suplentes las líneas de salida de los comandos de destino.

while IFS= read -r line; do 
  tr 1 a <<<"$line"
  tr 1 b <<<"$line"
done < <(echo '123')

Unix (GNU Paralelo)

La instalación de GNU parallel permite una solución robusta con serializada (por orden) de salida que, además, permite la ejecución en paralelo:

$ echo '123' | parallel --pipe --tee {} ::: 'tr 1 a' 'tr 1 b'
a23
b23

parallel por defecto, asegura que la salida de los diferentes comandos no interleave (este comportamiento puede ser modificado - ver man parallel).

Nota:Algunas distribuciones de Linux vienen con un diferentes parallel la utilidad, que no funciona con el comando de arriba;uso parallel --version para determinar cuál, si alguna, que usted tiene.


Windows

Jay Bazuzi útil respuesta muestra cómo hacerlo en PowerShell.Que dijo:su respuesta es el análogo de la reproducción en bucle bash respuesta anterior, será excesivamente lento con grandes conjuntos de entrada y también suplentes las líneas de salida de los comandos de destino.



bash-basado, pero de lo contrario portátil Unix solución con la ejecución sincrónica y salida de la serialización

El siguiente es un simple, pero razonablemente robusta implementación del enfoque presentado en tzot la respuesta que además proporciona:

  • de ejecución sincrónico
  • serializada (agrupados) de salida

Aunque no es estrictamente POSIX, porque es un bash secuencia de comandos, debe ser portable a cualquier plataforma Unix que ha bash.

Nota:Usted puede encontrar una más plena implementación liberado bajo la licencia MIT en esta Esencia.

Si usted guarde el código siguiente script fanout, y hacerlo ejecutable y poner su int PATH, el comando de la cuestión funcionaría de la siguiente manera:

$ echo 123 | fanout 'tr 1 a' 'tr 1 b'
# tr 1 a
a23
# tr 1 b
b23

fanout código fuente del script:

#!/usr/bin/env bash

# The commands to pipe to, passed as a single string each.
aCmds=( "$@" )

# Create a temp. directory to hold all FIFOs and captured output.
tmpDir="${TMPDIR:-/tmp}/$kTHIS_NAME-$$-$(date +%s)-$RANDOM"
mkdir "$tmpDir" || exit
# Set up a trap that automatically removes the temp dir. when this script
# exits.
trap 'rm -rf "$tmpDir"' EXIT 

# Determine the number padding for the sequential FIFO / output-capture names, 
# so that *alphabetic* sorting, as done by *globbing* is equivalent to
# *numerical* sorting.
maxNdx=$(( $# - 1 ))
fmtString="%0${#maxNdx}d"

# Create the FIFO and output-capture filename arrays
aFifos=() aOutFiles=()
for (( i = 0; i <= maxNdx; ++i )); do
  printf -v suffix "$fmtString" $i
  aFifos[i]="$tmpDir/fifo-$suffix"
  aOutFiles[i]="$tmpDir/out-$suffix"
done

# Create the FIFOs.
mkfifo "${aFifos[@]}" || exit

# Start all commands in the background, each reading from a dedicated FIFO.
for (( i = 0; i <= maxNdx; ++i )); do
  fifo=${aFifos[i]}
  outFile=${aOutFiles[i]}
  cmd=${aCmds[i]}
  printf '# %s\n' "$cmd" > "$outFile"
  eval "$cmd" < "$fifo" >> "$outFile" &
done

# Now tee stdin to all FIFOs.
tee "${aFifos[@]}" >/dev/null || exit

# Wait for all background processes to finish.
wait

# Print all captured stdout output, grouped by target command, in sequences.
cat "${aOutFiles[@]}"

Desde @dF:mencionó que PowerShell tiene tee, he pensado que me gustaría mostrar una manera de hacer esto en PowerShell.

PS > "123" | % { 
    $_.Replace( "1", "a"), 
    $_.Replace( "2", "b" ) 
}

a23
1b3

Tenga en cuenta que cada uno de los objetos provenientes del primer comando es procesado antes de que el siguiente se crea un objeto.Esto puede permitir el escalado a muy grandes entradas.

otra forma de hacerlo sería,

 eval `echo '&& echo 123 |'{'tr 1 a','tr 1 b'} | sed -n 's/^&&//gp'`

salida:

a23
b23

no necesitas crear una subshell aquí

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top