从 shell 脚本读取然后写入 c 程序

read then write to c programme from shell script

我有一个简短的 c 程序,输出 3 行然后从标准输入读取,我想使用 shell 脚本与其交互,如下所示:

  1. 逐行读取 c 输出 while read line; do ...; done <$out_
  2. 保存第二行if [ "$count" == 2 ]; then input=$line; fi
  3. 将保存的行发送回程序 echo $input >$in_

我在 shell 脚本中使用命名管道来重定向 c 程序的输出和输入:

./iotest >$out_ <$in_ &

但我没有得到预期的结果。

问题

在使用 mkfifo 创建管道 out_in_ 之后,我有一个 while read 循环,但它似乎会导致死锁。未读取任何内容,shell 脚本卡在读取命令上。

我尝试在启动 iotest 之前添加 cat >$in_ &,正如 here 所提议的那样,以打开 in_ 进行写入而不实际写入任何内容。这会解除对读取的阻塞,但不允许我输入 in_(我的 c 程序就像我将 \x00 写入标准输入一样,并且 echo $input >$in_ 命令挂起)。

我试过的

我已经读过 here 如果没有写入器则读取块,并且没有 cat >$in_ & 我有 iotest 写入 out_,并且 read 从中读取,我试图将 in_ 重定向到 iotest 而不是从一开始就向它写入任何内容。

但是为什么加了cat命令导致c程序读取失败,我就不知道了。如果我将 cat >$in_ &while true; do :; done >$in_&exec 3>$in_ & 交换(exec 3>$in_,如提议的 here,也会挂起),也会发生同样的情况。

代码

这是我的 C 代码:

$ cat iotest.c 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main (int argc, char* argv[])
{
    char A[9] = "";
    printf("hello. please send me the following line:\n");
    printf("input me\n");
    printf("now send me the line:\n");
    int ret = read(0,A,8);
    fprintf(stderr,"read returned %i\n",ret);
    if(ret != -1) {
        printf("You sent: %s\n", A);
        if (strcmp(A,"input me") == 0) {
            printf("correct!\n");
        } else {
            printf("wrong line.\n");
        }
    } else {
        printf("read failed.\n");
    }
    return 0;
}

这是我测试过的 shell 脚本:

$ cat runiotest.sh 
#!/bin/bash

in_=/tmp/testpipein
out_=/tmp/testpipeout

mkfifo $in_
mkfifo $out_
count=0
input=""

./iotest >$out_ <$in_ &
while read line
do
    count=$((count+1))
    echo "line number $count:"
    echo $line
    if [ "$count" == 2 ]
    then
        input=$line
    fi
    if [ "$count" == 3 ]
    then
        echo "$input" >$in_
    fi
done < $out_

rm -f $in_
rm -f $out_

我已经在 32 位 debian 和 64 位 ubuntu 上测试过,使用 gcc 编译,并使用 bash.

执行 shell 脚本

结果

没有添加 cat 命令:

$ /bin/bash/ ./runiotest.sh
(hangs)

加上 cat >$in_ &:

$ /bin/bash ./runiotest.sh
read returned 0
line number 1:
hello. please send me the following line:
line number 2:
input me
line number 3:
now send me the line:
line number 4:
You sent:
line number 5:
wrong line.

总结

所以我的问题是:我的 shell 脚本有什么问题,我如何修改它以读取然后写入我的 c 程序?我无法更改 C 代码本身,但可以 fiddle 使用 shell 脚本。

感谢您的帮助!

考虑:

./iotest >$out_ <$in_ &
while read line; do ...; done < $out_

这里,subshell 尝试打开 $out_ 用于写入和 $in_ 用于读取,它甚至不会执行 iotest 直到这两个 fifos 都打开。如果没有您添加的 cat,subshell 将无法打开 $in_ 直到有一些编写器,所以 iotest 甚至永远不会开始执行。当你添加 cat 时,子 shell 可以打开 $in_,而主 shell 充当 $out_ 的 reader,所以 subshell 能够打开两个 fifos 并执行 iotest。至于您看到的行为,这是 iotest.c 中的错误。打印 read 返回的值(到 stderr!),包括 read 的声明,并尝试使用 != -1 而不是 > -1

问题是c 程序输出的缓冲。使用 stdbuf 删除缓冲解决了它:

cat >$in_ &
stdbuf -o0 ./iotest <$out_ >$in_ &

通过此更改,输出应该是这样的:

$ /bin/bash ./runiotest.sh 
line number 1
hello. please send me the following line:
line number 2
input me
line number 3
now send me the line:
read returned 8
line number 4
You sent: input me
line number 5
correct!

补充一下,如果可执行文件设置了 setuid 位,则使用 stdbuf 不起作用。在这种情况下,您可以使用 expect 实用程序中的 unbuffer