我有两个shell scipts:carrier.sh和pie.sh以及两个命名管道:文件夹中的Answer和Query。 carrier.sh:
while read a
do
if [ "$a" = 'exit' ] ; then exit 0
fi
echo $a
#echo $a >> troll.txt
done
pie.sh:
( ./carrier.sh > Answer ) < Query &
echo 'Foo' > Query
read x < Answer
echo $x
echo 'Boo' > Query
read x < Answer
echo $x
echo 'exit' > Query
让我们试试:
$ ./pie.sh
Foo
...
&lt; - 它在此等待输入而不是打印Boo并以新提示结束。当我添加注释行时,它可以正常工作。这是为什么?我认为这可能是由于管道堵塞,缓冲或不冲洗造成的。我的初衷是在一个会话中从shell脚本与mysql-server进行通信(并且存在同样的问题)但是对于它我可以得到像“使用php”这样的答案:p。我只能在pie.sh中更改我的代码?
答案 0 :(得分:0)
FIFO是一种奇特的生物。一旦打开写入出口的唯一进程,读者就会获得EOF并且必须重新打开管道以进行读取以获得更多输入。因此,在您的脚本中,carrier.sh
在阅读并回显Foo
后退出。您的pie.sh
尝试再次打开FIFO进行写入,并且等待一个不存在的读者再次打开它。
我将carrier.sh
修改为:
while read a
do
if [ "$a" = 'exit' ] ; then exit 0
fi
echo $a
#echo $a >> troll.txt
done
echo "$0: out of here" >&2
输出(在Mac上运行macOS High Sierra 10.13.1和古老的Bash 3.25)是:
$ bash pie.sh
./carrier.sh: out of here
Foo
pie.sh: line 6: Answer: Interrupted system call
Foo
并且输出冻结。
我们如何解决这个问题?这实际上非常棘手。名义上,所有&#39;所要求的是告诉carrier.sh
它要读取哪个FIFO以及它要写入哪个FIFO,因此它可以每次打开它们。这意味着您需要这样的代码:
<强> carrier.sh
强>
input=$1
output=$2
while true
do
echo "$0: ($$) about to enter inner loop" >&2
while read a
do
if [ "$a" = 'exit' ]
then
echo "$0: ($$) exit" >&2
exit 0
fi
echo $a > $output
echo "$0: ($$) $a echoed back to standard output" >&2
done < $input
echo "$0: ($$) inner loop complete" >&2
done
echo "$0: ($$) out of here" >&2
<强> pie.sh
强>
#( ${SHELL:-bash} ./carrier.sh > Answer ) < Query &
rm -f Query Answer
mkfifo Query Answer
( ${SHELL:-bash} ./carrier.sh Query Answer ) &
echo "$0 ($$): at work"
echo 'Foo' > Query
read x < Answer
echo $x
echo 'Boo' > Query
read x < Answer
echo $x
echo 'exit' > Query
echo "$0 ($$): finished"
示例运行:
$ bash pie.sh
pie.sh (65389): at work
./carrier.sh: (65392) about to enter inner loop
./carrier.sh: (65392) Foo echoed back to standard output
./carrier.sh: (65392) inner loop complete
./carrier.sh: (65392) about to enter inner loop
Foo
./carrier.sh: (65392) Boo echoed back to standard output
./carrier.sh: (65392) inner loop complete
Boo
./carrier.sh: (65392) about to enter inner loop
pie.sh (65389): finished
./carrier.sh: (65392) exit
$ ps
PID TTY TIME CMD
782 ttys000 0:03.59 -bash
798 ttys001 0:00.03 -bash
821 ttys002 0:03.27 -bash
$
我在测试(早期版本)代码时出现了一些奇怪的奇怪行为,各种进程意外地挂了。我之前几分钟编辑的运营商脚本的版本继续响应,这让我很困惑。这就是为什么输出中有ps
个列表的原因;它表明我用来测试的ttys002
shell没有剩下的孩子。与我得到的早期(混乱)状态形成对比:
$ ps
PID TTY TIME CMD
782 ttys000 0:03.59 -bash
798 ttys001 0:00.03 -bash
821 ttys002 0:03.20 -bash
65214 ttys002 0:00.00 bash pie.sh
65258 ttys002 0:00.01 ksh -x carrier.sh
65296 ttys002 0:00.00 /bin/bash -x carrier.sh
65304 ttys002 0:00.00 /bin/bash carrier.sh
65316 ttys002 0:00.00 bash pie.sh
$ kill 65316 65304 65296 65258 65214
$
这个混乱是carrier.sh
的调试输出包含PID的部分原因 - 当我在编辑脚本以包含它之后看到没有PID的消息时,我最终解决了这个问题。中断并不会杀死一切。很明显如何/为什么65316幸免于我的中断,我不确定;同上65216. carrier.sh
的各种形式也许不那么令人惊讶。在运行测试之前,请确保您的测试环境是干净的。
另一种可能的方法来修复&#39;问题是pie.sh
启动一个脚本,适当地打开FIFO,然后睡觉(不读或写)。它必须在后台运行。这使FIFO保持开放,主要过程可以更自由地工作。后台进程将在pie.sh
退出时被终止。如果您对此进行调查,则需要仔细考虑后台进程是否打开FIFO以进行读取,写入或两者兼而有之。我没有探究过这个问题,但它应该有效 - 但是如果你尝试的话,请谨慎对待你的设置。 (困难的部分是确保开放的操作完成;开放的阅读不会完成,直到有一个作家,并且开放的写作不会完成,直到有读者。)确保你没有意外地挂着流浪的过程。
答案 1 :(得分:0)
我有Ubuntu 16.04和gnu bash版本4.3.48(1) - 发布。我对carrier.sh的第一个小变化的回答是:
$ ./pie.sh
Foo
./carrier.sh: out of here
...
&lt; - 等待输入
后面的脚本(有效)的效果非常荒谬,因为每个正在运行的pie.sh都有来自./carrier.sh的另一组回声。这与Foo一样无关紧要,之后就是Boo。它给了我一个暗示这个解决方案与我原来的pie.sh和在carrier.sh中的变化也有效:
while true
do
while read a
do
if [ "$a" = 'exit' ] ; then exit 0
fi
echo $a
#echo $a >> troll.txt
done
#echo "$0: out of here" >&2
done
文件carrier.sh就是这样一个黑盒子,代表不同的内部shell,比如mysql shell(在另一个版本的pie.sh中我有'mysql -b'而不是'./carrier.sh')。它的行为与carrier.sh的坏版本相同。这表明许多依赖于这个黑盒子的实现(不一定在shell中完成)。谢谢,您的回答有助于理解命名管道和重定向。每次删除和创建新的FIFO都很重要吗?