bash: 파이프 스크립트의 tty에서 읽기

bash: 파이프 스크립트의 tty에서 읽기

작업 중인 두 개의 bash 스크립트에 상당한 문제가 있습니다.

script.sh

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

파이프.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) 

출력을 실행 script.sh하고 파이핑 하면 pipe.sh다음과 같은 출력이 나타납니다.

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

보시다시피 4번 라인에 도달할 때까지 모든 것이 작동하는 것 같습니다. 저는 4번 라인이 pipe: write: var: 321에서 나올 것으로 예상했습니다 pipe.sh. 대신 에서 프롬프트를 받습니다 script.sh.

문자열 "456"을 입력하면 이전에 예상했던 줄이 실행되지만 잘못된 문자열이 포함됩니다(예상: "321", "456" 발생). 또한 6행에서는 "456"이 아닌 "321"이 인쇄됩니다.

여기서 뭔가가 완전히 잘못되었습니다. 이 문제를 해결하는 방법과 이런 일이 발생하는 이유에 대한 제안이 있으십니까?

업데이트:

본질적으로 나는 파이프가 아래 코드와 같은 방식으로 작동하기를 원합니다.

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

script2.sh그러나 나는 script1.sh. script2.sh인수로 전달할 수 있지만 script1.sh처음에는 파이프가 더 나은 솔루션이 될 것이라고 생각했습니다.

답변1

현재 터미널에서 및 읽기 read -p를 모두 호출 하고 파이프라인의 명령이 병렬로 실행되므로 사용자가 입력한 데이터를 먼저 가져오는 명령이 무엇인지 가정할 수 없습니다.script.shpipe.sh

from read -pscript.sh프롬프트를 표시할 수 있지만 사용자가 입력한 문자열은 read -pfrom 에서 읽을 수 있으며 pipe.sh그 반대의 경우도 마찬가지입니다.

a | b와 같은 파이프라인에서는 더 진행하기 전에 b쉽게 입력을 기다리도록 할 수 있지만 a그 반대는 사실이 아닙니다. 파이프가 버퍼링하고 있기 때문에 데이터를 읽지 않는다는 사실을 a알아차리기 전에 많은 양의 데이터를 써야 합니다 .b

이를 해결하는 한 가지 방법은 일종의 "순환 파이프라인"에서 stdout을 bstdin과 연결하고 ( ) 처럼 stdin의 입력도 기다리도록 수정하는 것 입니다. aascript.shbpipe.sh

쉘 언어의 제한으로 인해 다음을 사용해야 합니다.명명된 파이프그에 대한. 간단한 예:

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$

그만큼circpipe스크립트는 일반 쉘 명령을 허용하고 "꼬리"도 루프에서 벗어날 수 있는 보다 일반적인 도구로 만들 수 있습니다.

위의 예와 달리 기본적으로 루프를 "시작"하지 않습니다. 이를 위해서는 -command인수를 사용해야 합니다. 사용 예:

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

서파이프

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

관련 정보