bash: lendo tty em script canalizado

bash: lendo tty em script canalizado

Estou tendo alguns problemas com dois scripts bash nos quais estou trabalhando:

script.sh

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

tubo.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) 

Ao executar script.she canalizar a saída, pipe.shobtenho a seguinte saída:

$ ./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 você pode ver, tudo parece funcionar até chegar à linha 4. Eu esperava que a linha 4 fosse pipe: write: var: 321de pipe.sh. Em vez disso, recebo o prompt de script.sh.

Ao inserir a string "456" a linha anteriormente esperada é executada, mas com a string errada (esperado: "321", obtido "456"). Além disso, a linha 6 não imprime "456", mas sim "321".

Algo está totalmente errado aqui. Alguma sugestão sobre como corrigir isso e por que isso está acontecendo?

Atualizar:

Essencialmente, gostaria que o pipe funcionasse da mesma maneira que o código abaixo.

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}"

No entanto, não quero codificar script2.shem script1.sh. Eu poderia passar script2.shcomo argumento, script1.shmas inicialmente pensei que um cano seria uma solução melhor.

Responder1

As read -pchamadas em ambos script.shsão pipe.shlidas no terminal atual e, como os comandos em um pipeline são executados em paralelo, você não pode fazer suposições sobre qual deles será o primeiro a capturar os dados inseridos pelo usuário.

O read -pfrom script.shpode exibir seu prompt, mas a string inserida pelo usuário pode ser lida pelo read -pfrom pipe.she vice-versa.

Em um pipeline como a | b, bpoderia ser facilmente feito esperar pela entrada aantes de prosseguir, mas o inverso não é verdadeiro: como os pipes estão armazenando em buffer, aseria necessário gravar muitos dados antes de perceber que bnão está lendo nada deles.

Uma maneira de sair disso é conectar o stdout of bcom o stdin of aem uma espécie de "pipeline circular" e modificar a( script.sh) para também aguardar a entrada do stdin, assim como b( pipe.sh) faz.

Devido às limitações da linguagem shell, você deve usar umtubo nomeadopor isso. Exemplo simples:

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$

Ocircpipescript poderia ser transformado em uma ferramenta mais geral, que aceitaria comandos shell regulares e onde sua "cauda" também poderia sair do loop.

Ao contrário do exemplo acima, isso não irá "iniciar" o loop por padrão; para isso, um -commandargumento deve ser usado. Exemplo de uso:

./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

circuito

#! /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

informação relacionada