tuberias salida pipes lenguaje guardar filtros ejemplos comando archivo bash

bash - salida - tuberias en linux ejemplos



Cómo hacer un circuito de tuberías en bash (7)

Supongamos que tengo los programas P0 , P1 , ... P(n-1) para algunos n > 0 . ¿Cómo puedo redireccionar fácilmente la salida del programa Pi al programa P(i+1 mod n) para todo i ( 0 <= i < n )?

Por ejemplo, digamos que tengo un square programa, que lee repetidamente un número y luego imprime el cuadrado de ese número, y un programa de calc , que a veces imprime un número después del cual espera poder leer el cuadrado del mismo. ¿Cómo conecto estos programas de modo que cada vez que calc imprima un número, los cuadrados lo devuelvan a calc ?

Editar: probablemente debería aclarar lo que quiero decir con "fácilmente". La solución pipe / fifo nombrada es una que realmente funciona (y he utilizado en el pasado), pero en realidad requiere bastante trabajo para hacerlo correctamente si se compara con el uso de una tubería bash. (Debe obtener un nombre de archivo aún no existente, crear una tubería con ese nombre, ejecutar el "loop de tubería", limpiar la tubería con nombre). Imagine que ya no puede escribir prog1 | prog2 prog1 | prog2 y siempre tendrá que usar canalizaciones con nombre para conectar programas.

Estoy buscando algo que es casi tan fácil como escribir un tubo "normal". Por ejemplo, algo así como { prog1 | prog2 } >&0 { prog1 | prog2 } >&0 sería genial.


Canalizaciones con nombre.

Crea una serie de fifos, usando mkfifo

es decir, fifo0, fifo1

Luego adjunte cada proceso en término a las tuberías que desee:

procesado <fifo (n-1)> fifon


Dudo que sh / bash pueda hacerlo. ZSH sería una mejor opción, con sus funciones MULTIOS y coproc.


Esta es una pregunta muy interesante. Yo (vagamente) recuerdo una tarea muy similar en la universidad hace 17 años. Tuvimos que crear una serie de tuberías, donde nuestro código obtendría manejadores de archivos para la entrada / salida de cada tubería. Entonces el código bifurca y cierra los manejadores de archivos no utilizados.

Estoy pensando que podrías hacer algo similar con tubos con nombre en bash. Use mknod o mkfifo para crear un conjunto de tuberías con nombres únicos a los que pueda hacer referencia y luego bifurque su programa.


Una tubería con nombre podría hacerlo:

$ mkfifo outside $ <outside calc | square >outside & $ echo "1" >outside ## Trigger the loop to start


Después de pasar bastante tiempo ayer tratando de redirigir stdout a stdin , terminé con el siguiente método. No es realmente agradable, pero creo que lo prefiero sobre la solución pipe / fifo nombrada.

read | { P0 | ... | P(n-1); } >/dev/fd/0

El { ... } >/dev/fd/0 es para redirigir stdout a stdin para la secuencia de tubería como un todo (es decir, redirige la salida de P (n-1) a la entrada de P0). Usar >&0 o algo similar no funciona; esto es probablemente porque bash asume que 0 es de solo lectura mientras que no le importa escribir en /dev/fd/0 .

El canal de read inicial es necesario porque sin él, tanto el descriptor de archivo de entrada como el de salida son el mismo dispositivo de pts (al menos en mi sistema) y la redirección no tiene ningún efecto. (El dispositivo pts no funciona como una tubería, escribir en él pone cosas en su pantalla.) Al hacer la entrada de { ... } una tubería normal, la redirección tiene el efecto deseado.

Para ilustrar con mi ejemplo de calc / square :

function calc() { # calculate sum of squares of numbers 0,..,10 sum=0 for ((i=0; i<10; i++)); do echo $i # "request" the square of i read ii # read the square of i echo "got $ii" >&2 # debug message let sum=$sum+$ii done echo "sum $sum" >&2 # output result to stderr } function square() { # square numbers read j # receive first "request" while [ "$j" != "" ]; do let jj=$j*$j echo "square($j) = $jj" >&2 # debug message echo $jj # send square read j # receive next "request" done } read | { calc | square; } >/dev/fd/0

Ejecutando el código anterior se obtiene el siguiente resultado:

square(0) = 0 got 0 square(1) = 1 got 1 square(2) = 4 got 4 square(3) = 9 got 9 square(4) = 16 got 16 square(5) = 25 got 25 square(6) = 36 got 36 square(7) = 49 got 49 square(8) = 64 got 64 square(9) = 81 got 81 sum 285

Por supuesto, este método es bastante pirateo. Especialmente la parte de read tiene un efecto secundario no deseado: la terminación del circuito de tubería "real" no conduce a la terminación del conjunto. No pude pensar en nada mejor que read ya que parece que solo puedes determinar que el ciclo de la tubería ha finalizado al tratar de escribir algo sobre él.


Una pila de comandos se puede componer como cadena de una matriz de comandos arbitrarios y se puede evaluar con eval. El siguiente ejemplo da el resultado 65536.

function square () { read n echo $((n*n)) } # ---------- end of function square ---------- declare -a commands=( ''echo 4'' ''square'' ''square'' ''square'' ) #------------------------------------------------------------------------------- # build the command stack using pipes #------------------------------------------------------------------------------- declare stack=${commands[0]} for (( COUNTER=1; COUNTER<${#commands[@]}; COUNTER++ )); do stack="${stack} | ${commands[${COUNTER}]}" done #------------------------------------------------------------------------------- # run the command stack #------------------------------------------------------------------------------- eval "$stack"


Mis soluciones usan pipexec (la mayoría de la implementación de funciones proviene de su respuesta):

square.sh

function square() { # square numbers read j # receive first "request" while [ "$j" != "" ]; do let jj=$j*$j echo "square($j) = $jj" >&2 # debug message echo $jj # send square read j # receive next "request" done } square $@

calc.sh

function calc() { # calculate sum of squares of numbers 0,..,10 sum=0 for ((i=0; i<10; i++)); do echo $i # "request" the square of i read ii # read the square of i echo "got $ii" >&2 # debug message let sum=$sum+$ii done echo "sum $sum" >&2 # output result to stderr } calc $@

El comando

pipexec [ CALC /bin/bash calc.sh ] [ SQUARE /bin/bash square.sh ] / "{CALC:1>SQUARE:0}" "{SQUARE:1>CALC:0}"

El resultado (igual que en tu respuesta)

square(0) = 0 got 0 square(1) = 1 got 1 square(2) = 4 got 4 square(3) = 9 got 9 square(4) = 16 got 16 square(5) = 25 got 25 square(6) = 36 got 36 square(7) = 49 got 49 square(8) = 64 got 64 square(9) = 81 got 81 sum 285

Comentario: pipexec fue diseñado para iniciar procesos y construir tuberías arbitrarias en el medio. Debido a que las funciones de bash no se pueden manejar como procesos, existe la necesidad de tener las funciones en archivos separados y usar un bash separado.