作業している2つのbashスクリプトに問題があります。
スクリプトファイル
#!/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.sh
でpipe.sh
行われ、パイプラインのコマンドは並列に実行されるため、どのコマンドがユーザーが最初に入力したデータを取得するかを推測することはできません。
fromはプロンプトを表示できますが、ユーザーが入力した文字列はfromから読み取ることができ、read -p
その逆も同様です。script.sh
read -p
pipe.sh
このようなパイプでは、a | b
続行する前にb
入力を待つのは簡単ですが、a
その逆の場合ではありません。パイプがバッファリングされているため、a
読み取ったデータがないことを知る前に、多くのデータを作成する必要があります。b
この問題を解決する1つの方法は、b
stdoutとstdinをある種の「ループパイプ」で接続し、()のようにstdinの入力を待つように変更することです。 a
a
script.sh
b
pipe.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