如何解决在 Linux 上实现双向管道
我们想在我们的程序中执行一个交互式命令行实用程序(Samba smbclient)。 smbclient 就像一个典型的交互式 ftp 客户端,你向它提供命令(在它的提示之后),它访问网络共享并返回结果。对于我们的程序,我们要输入一个命令,检索结果,输入下一个命令,等等..
这是我们的示例程序。 /root/testfifo
是已通过 mkfifo
在 bash 中创建的 fifo。
int main()
{
const char *cmd = "smbclient //machine/share \
-U domain/user%pwd > /root/testfifo";
FILE *out = popen(cmd,"w");
if (!out)
{
return 0;
}
// Write the command to smbclient via 1st pipe
fputs("ls\n",out);
fflush(out);
// Read the result from smbclient via 2nd pipe
FILE *in = fopen("/root/testfifo","r");
if (!in)
{
return 0;
}
char buf[1000];
// Pending here..
fgets(buf,1000,in);
fclose(in);
pclose(out);
return 0;
}
问题是,程序在 fgets
行等待来自 in
的输入。但是,如果我删除第二个管道 in
,首先运行我们的程序,然后打开一个单独的终端从同一个管道(例如,tail -f /root/testfifo
)读取,它不会阻塞并从 smbclient 打印正确的结果.
这是为什么?
解决方法
问题似乎是因为 smbclient 正在执行某种缓冲,因此只有在您执行 pclose(outpipe);
此带有 cat
的版本有效:
#include <cstdio>
int main()
{
#define cmd "{ sleep 10; cat; } > /tmp/testfifo"
FILE* outpipe = popen(cmd,"w");
if (!outpipe)
{
return 0;
}
// Write the command to smbclient via 1st pipe
fputs("Message sent\n",outpipe);
fflush(outpipe);
// Read the result from smbclient via 2nd pipe
FILE* inpipe = fopen("/tmp/testfifo","r");
if (!inpipe)
{
return 0;
}
char buf[1000];
// Pending here..
fgets(buf,1000,inpipe);
printf("This is the message received : [%s]\n",buf);
pclose(inpipe);
pclose(outpipe);
return 0;
}
,
以编程方式操作专为人类设计的用户界面不一定那么容易。通常,基于文本的 UI 更容易,但您似乎偶然发现了其中一个问题:如果 C 或 C++ 程序的标准流完全缓冲,如果程序确定它们未连接到交互式设备(通常表示终端)。通过 fopen()
和 popen()
打开的文件也是如此。
你的程序依赖于它自己的流是行缓冲还是非缓冲。您可以通过 setvbuf()
or setlinebuf()
:
setlinebuf(outpipe);
setlinebuf(inpipe);
但这可能还不够。如果 smbclient
使用 stdio 函数来读取命令和发出输出,那么它的一侧也会有缓冲,这不在您的控制范围内。
您真正需要的是一个用于运行 smbclient 的伪终端,这样它就好像在交互式运行一样。您可以推出自己的产品,但这类事情正是libexpect 的全部内容。我建议围绕使用此库来启动和与 smbclient
通信重新编写您的程序。作为奖励,您无需管理 fifo。
事实证明,这与 smbclient(或任何其他具有类似缓冲的应用程序)内部的 I/O 缓冲有关。
要解决这个问题,
const char *cmd =
"stdbuf -oL smbclient //machine/share \
-U domain/user%pwd > /root/testfifo";
更多读者参考: Turn-off-buffering-in-pipe
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。