¿Cómo componer funciones bash usando tuberías?

18

Tengo pocas funciones definidas de esta manera:

function f {
  read and process $1
  ...
  echo $result
}

Quiero componerlos juntos para que se vea la invocación f | g | h.

¿Qué idioma debo usar para convertir la función que trabaja en argumentos en argumentos de lectura de stdin? ¿Es posible leer pares, tuplas de argumentos de la secuencia sin necesidad de escapar de ellos (por ejemplo, terminación nula)?

Rumca
fuente
O desea algo así h(g(f(...)))o cada una de las funciones lee de la entrada estándar ( read x; ...) y escribe en la salida estándar ( echo ...).
vonbrand

Respuestas:

21

Un enfoque potencial sería colocar una while...readconstrucción dentro de sus funciones que procesara cualquier información que ingresara a la función a través de STDIN, la operara y luego emitiera los datos resultantes a través de STDOUT.

function X {
  while read data; do
    ...process...
  done
}

Se deberá tener cuidado con la forma en que configura sus while ..read..componentes, ya que dependerán en gran medida de los tipos de datos que podrán consumir de manera confiable. Puede haber una configuración óptima que se te ocurra.

Ejemplo

$ logF() { while read data; do echo "[F:$(date +"%D %T")] $data"; done; }
$ logG() { while read data; do echo "G:$data";                    done; }
$ logH() { while read data; do echo "H:$data";                    done; }

Aquí está cada función por sí misma.

$ echo "hi" | logF
[F:02/07/14 20:01:11] hi

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Aquí están cuando los usamos juntos.

$ echo "hi" | logF | logG | logH
H:G:[F:02/07/14 19:58:18] hi

$ echo -e "hi\nbye" | logF | logG | logH
H:G:[F:02/07/14 19:58:22] hi
H:G:[F:02/07/14 19:58:22] bye

Pueden tomar varios estilos de entrada.

#-- ex. #1
$ cat <<<"some string of nonsense" | logF | logG | logH
H:G:[F:02/07/14 20:03:47] some string of nonsense

#-- ex. #2    
$ (logF | logG | logH) <<<"Here comes another string."
H:G:[F:02/07/14 20:04:46] Here comes another string.

#-- ex. #3
$ (logF | logG | logH)
Look I can even
H:G:[F:02/07/14 20:05:19] Look I can even
type to it
H:G:[F:02/07/14 20:05:23] type to it
live
H:G:[F:02/07/14 20:05:25] live
via STDIN
H:G:[F:02/07/14 20:05:29] via STDIN
..type Ctrl + D to stop..

#-- ex. #4
$ seq 5 | logF | logG | logH
H:G:[F:02/07/14 20:07:40] 1
H:G:[F:02/07/14 20:07:40] 2
H:G:[F:02/07/14 20:07:40] 3
H:G:[F:02/07/14 20:07:40] 4
H:G:[F:02/07/14 20:07:40] 5

#-- ex. #5
$ (logF | logG | logH) < <(seq 2)
H:G:[F:02/07/14 20:15:17] 1
H:G:[F:02/07/14 20:15:17] 2
slm
fuente
4

Como una adición a la respuesta de slm , hice algunos experimentos con tuplas separadas por nulos como argumentos de función:

$ sayTuple() { 
    IFS= read -r -d $'\0' d1
    IFS= read -r -d $'\0' d2
    echo "sayTuple: -$d1- -$d2-"
}

Notas: sayTuplelee dos veces un registro terminado en nulo que -d $'\0'maneja cualquier espacio que rodea la entrada IFS=. echoregistros anteriores rodeados de-

El resultado muestra que maneja correctamente la entrada terminada en nulo que contiene \ny \t:

$ printf "%s\0%s\0" "Hello " $' Brave\n\tWorld' | sayTuple 
sayTuple: -Hello - - Brave
        World-

Agregue sugerencias para mejorar en los comentarios, es un tema interesante.

grebneke
fuente
A +1 le gusta tu idea. En su lugar, podríamos colocar un bucle que le permitiría tomar números arbitrarios de argumentos. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm
Parece que deberías poder hacer un IFS= read -r -d $'\0' -a argpero no pude hacer que esto funcione. Esto permitiría eliminar el while, lo que parece innecesario, pero fue el único patrón que pude poner a trabajar.
slm