bash: lectura de tty en script canalizado

bash: lectura de tty en script canalizado

Tengo bastantes problemas con dos scripts bash en los que estoy trabajando:

guión.sh

#!/bin/bash
while true; do
    read -p "script: read: var: " var
    echo "script: write: var: $var"
done

pipa.sh

#!/bin/bash
while read line; do
        echo "pipe: read: line: (( $line ))"
        read -p "pipe: read var: " var < /dev/tty
        echo "pipe: write: var: $var"
done< <(cat) 

Al ejecutar script.shy canalizar la salida, pipe.shobtengo el siguiente resultado:

$ ./script.sh | ./pipe.sh
1: script: read: var: 123   # user entering '123'
2: script: read: var: pipe: read: line: (( script: write: var: 123 ))
3: pipe: read var: 321      # user entering '321'
4: script: read: var: 456   # user entering '456'
5: pipe: write: var: 456
6: pipe: read: line: (( script: write: var: 321 ))
7: pipe: read var: 

Como puede ver, todo parece funcionar hasta llegar a la línea 4. Esperaba que la línea 4 fuera pipe: write: var: 321de pipe.sh. En lugar de eso, recibo el mensaje de script.sh.

Al ingresar la cadena "456", se ejecuta la línea esperada anteriormente, pero con la cadena incorrecta (esperada: "321", obtuve "456"). Además, la línea 6 no imprime "456" sino "321".

Algo anda totalmente mal aquí. ¿Alguna sugerencia sobre cómo solucionar este problema y por qué sucede?

Actualizar:

Básicamente, me gustaría que la tubería funcione de la misma manera que el siguiente código.

script1.sh

#!/bin/bash
while true; do
  read -p "val1: " val1
  script2.sh "${val1}"
done

script2.sh

#!/bin/bash
val1="${1}"
read -p "val2: " val2
echo "${val1} ${val2}"

Sin embargo, no quiero codificar script2.shen script1.sh. Podría pasarlo script2.shcomo argumento, script1.shpero inicialmente pensé que una tubería sería una mejor solución.

Respuesta1

Las read -pllamadas en ambos script.shse pipe.shleen desde la terminal actual, y dado que los comandos en una canalización se ejecutan en paralelo, no se pueden hacer suposiciones sobre cuál de ellos es el primero en arrebatar los datos ingresados ​​por el usuario.

El read -premitente script.shpuede mostrar su mensaje, pero el remitente puede leer la cadena ingresada por el usuario read -py pipe.shviceversa.

En una tubería como a | b, bse podría hacer fácilmente esperar la entrada aantes de continuar, pero lo contrario no es cierto: dado que las tuberías están almacenando en búfer, atendría que escribir una gran cantidad de datos antes de darse cuenta de que bno está leyendo ninguno de ellos.

Una forma de solucionar esto es conectar la salida estándar de bcon la entrada estándar de aen una especie de "tubería circular" y modificar a( script.sh) para que también espere la entrada de la entrada estándar, tal como lo hace b( pipe.sh).

Debido a las limitaciones del lenguaje shell, debes usar untubería con nombrepara eso. Ejemplo sencillo:

cat > circpipe <<'EOT'; chmod 755 circpipe
fifo=$(mktemp -u)
mkfifo "$fifo" || exit 1
exec 3<>"$fifo" >"$fifo" <"$fifo"
rm "$fifo"
echo trigger
"$1" | "$2"
EOT

cat > pipe-head <<'EOT'; chmod 755 pipe-head
while read next; do
        read -p "HEAD's prompt>> " var </dev/tty || exit
        echo "$var"
done
EOT

cat > pipe-tail <<'EOT'; chmod 755 pipe-tail
while read input; do
        echo >&2 "  TAIL'input: $input"
        read -p "  TAIL's prompt>> " var </dev/tty
        echo >&2 "  TAIL processing <$var>"
        echo next       # trigger the head of the pipeline
done
EOT
./circpipe ./pipe-head ./pipe-tail
HEAD's prompt>> foo
  TAIL'input: foo
  TAIL's prompt>> bar
  TAIL processing <bar>
HEAD's prompt>> baz
  TAIL'input: baz
  TAIL's prompt>> quux
  TAIL processing <quux>
HEAD's prompt>> ^D$

ElcircpipeEl script podría convertirse en una herramienta más general, que aceptaría comandos de shell regulares y donde su "cola" también podría salirse del bucle.

A diferencia del ejemplo anterior, esto no "pondrá en marcha" el ciclo de forma predeterminada; para eso -commandse debe utilizar un argumento. Uso de ejemplo:

./circpipe -echo './pipe-head | stdbuf -oL sed s/e/o/g | ./pipe-tail'
HEAD's prompt>> pee
  TAIL'input: poo
  TAIL's prompt>> lol
  TAIL processing <lol>
HEAD's prompt>>^D

tubo de escape

#! /bin/sh
# usage: circpipe [-start_command] shell_command
# run 'shell_command' with its stdin connected to its stdout
# via a FIFO
# if 'start_command' is given, run it before `shell_command`
# with its stdout redirected to the same FIFO
[ "$#" -gt 0 ] || exit 0
fifo=$(mktemp -u)
mkfifo "$fifo" || exit 1
exec 3<>"$fifo" >"$fifo" 3<"$fifo"
rm "$fifo"
case $1 in -*)  eval "${1#-}"; shift; esac
IFS='|'; eval "<&3 $* &"
exec >&-
wait

información relacionada