Una función de caparazón. Esto funcionará en cualquier shell que admita <<<
here-strings, incluidos zsh y bash.
xyz() {
while read line
do
printf "%s " "$line"
"[email protected]" <<<"$line"
done
}
$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
Habrá muchos problemas en la mayoría de los casos debido a la forma en que funciona el almacenamiento en búfer de stdio. Una solución para Linux podría ser usar el stdbuf
programe y ejecute el comando con coproc, para que pueda controlar explícitamente el intercalado de la salida.
Lo siguiente asume que el comando generará una línea después de cada línea de entrada.
#!/bin/bash
coproc stdbuf -i0 -o0 "[email protected]"
IFS=
while read -r in ; do
printf "%s " "$in"
printf "%s\n" "$in" >&${COPROC[1]}
read -r out <&${COPROC[0]}
printf "%s\n" "$out"
done
Si se necesita una solución más general, ya que el OP solo requiere cada línea de entrada al programa para eventualmente generar una línea en lugar de inmediatamente, entonces se necesita un enfoque más complicado. Crea un bucle de eventos usando read -t 0
para intentar leer desde stdin y el co-proceso, si tiene el mismo "número de línea" para ambos, luego envíe, de lo contrario almacene la línea. Para evitar usar el 100% de la CPU si en alguna ronda del bucle de eventos ninguno estaba listo, introduzca un pequeño retraso antes de ejecutar el bucle de eventos nuevamente. Hay complicaciones adicionales si el proceso genera líneas parciales, estas deben almacenarse en búfer.
Si se necesita esta solución más general, escribiría esto usando expect ya que ya tiene un buen soporte para manejar la coincidencia de patrones en múltiples flujos de entrada. Sin embargo, esta no es una solución bash/zsh.
En este caso particular, usaría perl
:
printf '%s\n' foo bar | perl -Mopen=locale -lpe '$_ .= " " . reverse$_'
foo oof
bar rab
Que podría extender para trabajar también con grupos de grafemas:
$ printf '%s\n' $'complique\u301' |
perl -Mopen=locale -lpe '$_ .= " " . join "", reverse/\X/g'
compliqué éuqilpmoc
O zsh
:
while IFS= read -r line; do
print -r -- $line ${(j::)${(s::Oa)line}}
done
Aunque evitaría usar while read
bucles para procesar texto, incluso en zsh
(incluso si en este caso particular, no es tan malo ya que solo se usan los comandos integrados).
En el caso general, usar archivos temporales es probablemente el mejor enfoque. Con zsh
, puedes hacerlo con:
(){paste -d ' ' $1 <(rev <$1)} =(print -l foo bar)
(donde =(...)
se encarga de crear y limpiar el archivo temporal).
Reemplazándolo con tuberías y alguna forma de tee
ing es una receta para el punto muerto en el caso general. Consulte estas preguntas similares para conocer algunos enfoques y detalles sobre las situaciones de interbloqueo:
- Dividir una entrada para diferentes comandos y combinar el resultado
- tee + cat:use una salida varias veces y luego concatene los resultados