命名管道停止工作

时间:2017-11-11 00:50:09

标签: shell named-pipes

我有两个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中更改我的代码?

2 个答案:

答案 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都很重要吗?