c - 强制刷新或读取未刷新的输出
问题描述
这是该工具的前端dc
;这个想法是键入一个中缀表达式 (2 + 3),将其映射到相应的后缀符号 (2 3 +) 并将其发送到dc
. 它是做什么的bc
。
我正在使用管道执行此操作,但前端挂起等待输出。
这是代码;我将在下面继续评论它。我的“h”命令dc
未实现,因此我正在寻找作为dc
.
TL; DR:该过程挂起,因为stdout
indc
没有刷新或者这就是我认为已经找到的。无论如何,我如何阅读它或在每次写入后强制刷新?
我发现的内容在下面评论。
#define DC_EOF_RCV "dc: 'h' (0150) unimplemented"
#define DC_EOF_SND "h\n"
static FILE *sndfp, *rcvfp;
int dcinvoke() {
int pfdout[2], pfdin[2];
pid_t pid;
pipe(pfdout);
pipe(pfdin);
switch (pid = fork()) {
case -1: exit(1);
case 0:
dup2(pfdout[0], STDIN_FILENO);
dup2(pfdin[1], STDOUT_FILENO);
close(pfdout[0]);
close(pfdout[1]);
close(pfdin[0]);
close(pfdin[1]);
execlp("dc", "dc", "-", NULL);
}
close(pfdout[0]);
close(pfdin[1]);
sndfp = fdopen(pfdout[1], "w");
rcvfp = fdopen(pfdin[0], "r");
return 1;
}
void dcsnd(const char *s) {
fputs(s, sndfp);
fflush(sndfp);
}
void dcrcv(char *buf, size_t max) {
fgets(buf, max, rcvfp); // <<<<< HANGS HERE
}
int turnaround() {
dcsnd(DC_EOF_SND); fflush(sndfp);
}
int rcvall() {
char buf[256];
turnaround();
for (;;) {
dcrcv(buf, sizeof(buf));
if (! strcmp(buf, DC_EOF_RCV)) {
break;
}
printf("%s", buf);
}
return 1;
}
int prompt(const char *msg, char *res, size_t resmax, int *eofp) {
char *p;
printf("\n%s ", msg);
if (!fgets(res, resmax, stdin)) {
*eofp = 1;
} else {
if (p = strrchr(res, '\n')) {
*p = 0;
}
*eofp = 0;
}
return 1;
}
int main() {
char buf[128], line[128];
int eof;
dcinvoke();
dcsnd("2 3 +\n");
dcsnd(DC_EOF_SND);
rcvall();
for (;;) {
prompt("Expression", buf, sizeof(buf), &eof);
if (eof) {
break;
}
snprintf(line, sizeof(line), "%s\n", buf);
dcsnd(line);
rcvall();
}
dcsnd("q\n");
return 0;
}
为了简单起见,我删除了错误检查。
前端挂在行:
fgets(buf, max, rcvfp);
因为我真的不知道如何找到问题,所以我写了自己的dc
,它什么都不做,只是正确响应“h”命令并将它得到的所有内容输出到文件中(所以我可以调试它;我不知道其他方法)。如果有用,我可以在这里添加。
我发现对fflush(stdout)
in (my)的调用会dc
恢复前端,因为对fgets
finally 的调用会返回。
我无法改变dc
自己。怎么能不挂fgets
?
我的一些想法:
使用另一个线程刷新
rcvfp
(stdout
ofdc
)使用伪终端编写它
我正在寻找建议或一种简单的方法来避免它们。
我尝试过了:
解决方案
dc 将错误消息发送到 stderr,而不是 stdout,因此您永远不会看到它们——它们直接发送到终端(或您环境中连接的任何 stderr)。为了能够在您的程序中读取它们,您还需要重定向 stderr。添加
dup2(pfdin[1], STDERR_FILENO);
在子叉代码中。
为了使您的代码真正起作用,我还必须在 DC_EOF_RCV 字符串的末尾添加一个换行符(以匹配 dc 发送的内容),并dcsnd(DC_EOF_SND);
从 main 中删除额外的调用,因为您永远不会在任何地方进行匹配它的接收。即使这样,您仍然可以在写入 stdout 和 stderr 之间获得 dc 内的竞争条件,这可能会导致输出线混合,从而导致响应与您的 DC_EOF_RCV 字符串不匹配。你可以通过在你的turnaround
函数中添加一个小睡眠来避免这些。
另一种选择是在主循环中使用 poll 同时从 stdin 和 dc 读取。有了这个,你不需要奇怪的“发送 h 命令来触发错误”机制来确定 dc 何时完成——你只需循环。您的主循环最终看起来像:
struct pollfd polling[2] = { { STDIN_FILENO, POLLIN, 0 }, { fileno(rcvfp), POLLIN, 0 } };
printf("Expression ");
for(;;) {
if (poll(polling, 2, -1) < 0) {
perror("poll");
exit(1); }
if (polling[0].revents) {
/* stdin is ready */
if (!fgets(buf, sizeof(buf), stdin))
break;
dcsnd(buf); }
if (polling[1].revents) {
/* dc has something for us */
printf("\r \r"); /* erase the previsouly printed prompt */
dcrcv(buf, sizeof(buf));
printf("%s\n", buf); }
printf("Expression "); /* new prompt */
}
您可以在此处管理错误条件(revents
可能是错误或挂断而不是POLLERR
正常输入 - 所有这些都是非零值,因此仅针对非零进行测试是合理的)。POLLHUP
POLLIN
推荐阅读
- r - 按降序计算字母频率
- php - How do i post a $_GET variable value using a REQUEST METHOD
- javascript - RTCmulticonnection 使用现有的 getUserMedia 流
- module - 如何动态构建对模块中函数的调用
- asp.net - Visual Studio 2017 在调试单个项目时启动两个 IE 实例
- javascript - 如何在 jQuery 中调用函数
- php - 如何使用 php 和 sql 在单个查询中插入多个数组
- javascript - 如何在警报中显示 console.log 输出
- visual-studio-code - 使用 babel 保留 jsdoc 注释和 VS Code 智能感知
- python - 输出不正确:从 pdf、docx 的 pptx 中提取文本不会在它们自己的 spearte 行中输出