如何让bash FIFO等到它从另一端读完?

时间:2014-01-13 01:01:45

标签: linux bash fifo

我正在编写一个bash脚本,我正在寻找能够将命令发送到程序将读取和执行的文件的东西。我找到了this。从一开始我开始尝试我意识到有一个很大的麻烦:如果输入行大约是100个字符,那么来自另一方的读数将有50%的可能性失败。让我举例说明会发生什么。

首先,我们需要分别为服务器和客户端提供一个fifo和两个one-liners。

# creating fifo
mkfifo pipe

# a-la server
while true; do \
    echo -n 'Send to client: > ';\
    read ;\
    echo "$REPLY" > pipe ;\
    read answer < pipe ;\
    echo "Got answer: $answer" ;\
done

# a-la client
while read < pipe; do \
    echo 'Finished reading.';\
    echo "FROM SERVER: $REPLY" ;\
    echo ACK > pipe ;\
done

假设我们在$ HOME中完成了所有这些操作,那么就不需要检查管道是否存在。那么,在“123”或“错误在哪里?”这样的简单情况下,它就可以了。但是,如果您尝试从维基百科上的任何文章中复制五行段落以将其发送到客户端,它将会失败。

我们可以得到的例子:

服务器:

Send to client: > 123
Got answer: ACK
Send to client: > авление Вашаклахуун-Уб’аах-К’авииля отметилось возведением множества построек в столице царства. Были сооружены Храм 16, Храм 20, Храм 21, Храм 22 (посвящённый двадцатилетию со дня коронации этого правителя[31]), окончательная версия местного стадиона для игры в мяч, а также святилище Эсмеральда, украшенное большой Иероглифической лестницей, излагающей историю Шукуупа. Стоит также отметить, что при Вашаклахуун-Уб’аах-К’авииле резко изменился стиль в монументальной скульптуре. Его стелы, установленные после 710 года (а именно Стелы C, F, 4, H, A, B и D), отличаются пластичностью и раскованно
Got answer: ÐÐм множе²°ÐÐÑÑÑÐÐÐ ²ÑÑÐÐÐÑÐ °²° »¸ÑÐÐÑÑÐÐÐÑ ¥°¼1,ÐÑÐÐ 0 ¥°¼2,ÐÑÐÐ 2(¿¾²½½¹ÐÐÐÐÑÐÑÐÐÐÑÐÑÑÐ ÐÐÑ º¾¾½°¸¸ÑÑÐÐÐ ¿°²¸µ»3], ÐÐÐÐÑÐÑÐÐÑÐÐÑ ²µ¸ÐÐÑÑÐÐÐÐ °´¸¾½°ÐÐÑ ¸³Ð ¼ °ÑÐÐÐÐ ²¸»¸µÐÑÐÐÑÐÐÑÐÐ,ÑÐÑÐÑÐÐÐÐÐ ±¾»¾¹ÐÐÑÐÐÐÐÑÐÑÐÑÐÐÐ »µ½¸µ¹ ¸·»°³°щµ¹ÐÑÑÐÑÐÑ ¨º¿° ¡¾¸ÑÐÐÐÐ ¾¼µ¸ ¾ÐÑÐ °°º»°½ÐÐ♰°Ð♰²¸¸»µÑÐÐÐÐ ¸·¼µ½¸»ÑÑлÐ ¼¾½¼µ½°»½¾¹ÑÐÑÐÑÐÑÑÑÐ.ÐÐÐ µ»ы, устан¾²»µ½½µÐÐÑÐÐ 1 ³¾´°(°ÐÐÐÐÐÐ ¡µ»C ,4 ,A  ¸D,ÐÑÐÐÑÐÑÑÑÑ ¿»°¸½¾Ð °º¾ÐÐÐÐ
Send to client: >

在客户端只有:

Finished reading.
FROM SERVER: 123

看起来服务器在发送数据后就开始阅读,因此客户端几乎无法从管道中读取内容。在上面的例子中,他无法阅读任何内容。这一切都会以多字节编码打破文本。

简单地说,它就像那样

  1. 服务器发送:aaaaaaaa… -this is a very long line - …aaaaaabcbcbcbc
  2. 服务器读取:aaaa… …abcbbc
  3. /实际上与#2 /客户端同时读取服务器尚未设法读取的内容:aaccb
  4. 为了让服务器在尝试从客户端读取“ACK”之前等待,我尝试更改管道的文件权限以使它们充当信号量。结果很有趣。

    由于我们无法操纵读取权限(因为一旦从服务器禁止,客户端将无法从管道中读取),我们将切换-w标志。

    以下是客户端和服务器的修改版本。 NB在客户端上读取命令后重定向更改。

    # a-la server
    chmod 644 pipe ;\
    while true; do \
        echo -n 'Send to client: > ';\
        read ;\
        echo "$REPLY" > pipe ;\
        chmod -w pipe ;\
        until [ -w pipe ]; do \
            echo 'Waiting client to read what we sent' ;\
            sleep 1 ;\
        done ;\
        read answer < pipe ;\
        echo "Got answer: $answer" ;\
    done
    
    # a-la client
    chmod 644 pipe ;\
    set -x ;\
    while read <>pipe; do \
        echo 'Finished reading.' ;\
        chmod u+w pipe ;\
        echo $? ;\
        ls -l pipe &>/dev/null ;\
        echo "FROM SERVER: $REPLY" ;\
        echo ACK > pipe ;\
    done
    

    chmod 644可以确保管道具有正确的权限,无论您首先启动哪个脚本。

    set -x,以便看到将会有的乐趣。

    由于管道上的读写锁定,客户端中的重定向已更改为<>。我不确定它们是如何工作的,但在服务器上禁用写入权限后,使用read <pipe在客户端上阅读不起作用。

    确保您的段落足够大,以便代码回显“等待客户端读取我们发送的内容”执行。

    我实际上并不打算发送那些大的数据,我只是认为I / O中的意外延迟可能是这个错误出现在短语中的原因。

    现在的问题是:为什么,如果删除ls命令,它会停止处理大数据?

    我的bash版本:GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)

1 个答案:

答案 0 :(得分:2)

命名管道只是一个单向通信流:任何进程写入其中的任何内容都将被某个进程读出。你有两个进程(服务器和客户端)写入它和两个进程读取它,所以你不能保证哪个进程看到哪个输入。

您需要双向通信:您希望客户端只查看服务器写入的内容,而您的服务器只能查看客户端写入的内容。为此,您需要创建两个命名管道:一个是服务器写入并且客户端从中读取,另一个是客户端写入并且服务器从中读取。