linux - 当第一个程序未在 BASH 中退出时使用 2 级管道出现问题
问题描述
考虑一下:
xinput --test 11 | grep "button press 1"
* 11是我的光学鼠标索引(可以是其他任何值),“按钮按下 1”表示左键单击。
当我单击屏幕中的某个位置时,它会显示:
button press 1
到目前为止没有问题。但是当我想使用它的输出作为我的 C 程序的输入时,我注意到在另一层管道之后程序的标准输入总是空的:
xinput --test 11 | grep "button press 1" | ./poll_test
这是我的poll_test代码:
/*This is just a program to test polling functionality on stdin.
I've used other programs instead of this as well.
None of them were able to read the stdin */
#include <fcntl.h>
#include <stdio.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <unistd.h>
void main(int argc, char ** argv) {
int fd;
char buf[1024];
int bytes_read;
struct pollfd pfds[1];
while (1) {
pfds[0].fd = 0;
pfds[0].events = POLLIN;
poll(pfds, 1, -1);
if (pfds[0].revents & POLLIN) {
bytes_read = read(0, buf, 1024);
printf("%s\n" , buf);
if (!bytes_read) {
printf("stdin closed\n");
return;
}
write(1, buf, bytes_read);
}
}
}
尽管有点击,但它什么也不打印。
这令人困惑。这不是正常行为。例如,当我运行它时:
ls | grep a | grep b
它成功地向我展示了结果。唯一的区别是ls
here 在打印到stdout后退出,但在xinput版本中并非如此。
我花了很多时间编写脚本以在鼠标单击时播放哔声,但这没有用。所以我想使用 C 程序,因为 bash 中没有轮询功能。
据我所知,bash 中管道的工作是这样的:
第二个程序(管道语句中的正确程序)被执行,直到它想从它的标准输入中读取,它停止继续前进,直到有东西可以从标准输入中读取。
考虑到这一点,我发布的命令中的第三个程序应该能够读取输出。就像第一个程序退出时的情况一样。
如果管道问题不起作用,下一步将是直接使用libxcb而不是命令xinput 。
我完全糊涂了。任何帮助将非常感激。
编辑:我也尝试使用中间文件描述符:
exec 3<&1
xinput --test 11 | grep -i "button press 1" >&3 | ./poll_test 3>&1
但没有帮助。而且强制刷新标准输出也不起作用:
xinput --test 11 | grep -i "button press 1" ; stdbuf -oL | ./poll_test
解决方案
似乎grep
正在根据输出是否为终端来改变其缓冲行为。我不知道为什么会发生这种情况,但--line-buffered
强制它使用行缓冲(一旦行结束就评估表达式):
xinput --test 11 | grep "button press 1" --line-buffered | ./poll_test