C 与子进程stdout/stdin通信
我试图与一个进程通信(该进程本身会写入stdin和stdout,以便在终端中与用户交互),并在C中读取它的stdin和写入它的stdout 因此,我尝试以编程方式替换shell用户。一个methapohrical示例:假设出于某种原因我想在C中使用VIM。然后我还需要编写命令(stdout)并从编辑器(stdin)中读取内容 起初我认为这可能是一项微不足道的任务,但似乎没有标准的方法<代码>int系统(const char*命令)代码>只执行一个命令,并将命令stdin/stdout设置为调用进程之一 因为这毫无意义,我查看了C 与子进程stdout/stdin通信,c,linux,ipc,stdout,pipe,C,Linux,Ipc,Stdout,Pipe,我试图与一个进程通信(该进程本身会写入stdin和stdout,以便在终端中与用户交互),并在C中读取它的stdin和写入它的stdout 因此,我尝试以编程方式替换shell用户。一个methapohrical示例:假设出于某种原因我想在C中使用VIM。然后我还需要编写命令(stdout)并从编辑器(stdin)中读取内容 起初我认为这可能是一项微不足道的任务,但似乎没有标准的方法int系统(const char*命令)只执行一个命令,并将命令stdin/stdout设置为调用进程之一 因为这
FILE*popen(constchar*命令,constchar*类型)代码>但手册页面说明:
根据定义,管道是单向的,因此类型参数只能指定读取或写入,不能同时指定读取或写入;结果流相应地为只读或只读
及其含义:
popen()的返回值在所有方面都是正常的标准I/O流,但它必须使用pclose()而不是fclose(3)关闭写入这样的流会写入标准输入
指挥部的职能命令的标准输出与调用popen()的进程的标准输出相同,除非命令本身对此进行了更改。相反,从“弹出”流读取
该命令的标准输出和标准输入与调用popen()的进程的标准输入相同
因此,使用popen()并非完全不可能,但在我看来它非常不雅观,因为我必须解析调用进程的stdout(调用popen()的代码),以便解析从popened命令发送的数据(当使用popen类型“w”时)
相反,当使用“r”类型调用popen时,我需要写入调用的进程stdin,以便将数据写入popened命令。在本例中,我甚至不清楚这两个进程是否在stdin中接收相同的数据
我只需要控制一个程序的stdin和stdout。我的意思是不能有如下函数:
stdin_of_process, stdout_of_process = real_popen("/path/to/bin", "rw")
// write some data to the process stdin
write("hello", stdin_of_process)
// read the response of the process
read(stdout_of_process)
因此,我的第一个问题是:实现上层功能的最佳方式是什么
目前,我正在尝试以下方法与另一个进程进行通信:
用int-pipe(int-fildes[2])设置两条管道代码>。一个管道读取进程的标准输出,另一个管道写入进程的标准输入
叉子
使用intexecvp(constchar*file,char*constargv[]),在分叉子进程中执行我想要与之通信的进程代码>
使用原始流程中的两个管道与子级进行通信
说起来很简单,bot的实现并不是那么简单(至少对我来说)。在一个案例中,我很奇怪地做到了这一点,但当我试图用一个更简单的例子来理解我在做什么时,我失败了。这是我目前的问题:
我有两个节目。第一个只需每隔100毫秒将递增的数字写入其标准输出:
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void sleepMs(uint32_t ms) {
struct timespec ts;
ts.tv_sec = 0 + (ms / 1000);
ts.tv_nsec = 1000 * 1000 * (ms % 1000);
nanosleep(&ts, NULL);
}
int main(int argc, char *argv[]) {
long int cnt = 0;
char buf[0x10] = {0};
while (1) {
sleepMs(100);
sprintf(buf, "%ld\n", ++cnt);
if (write(STDOUT_FILENO, buf, strlen(buf)) == -1)
perror("write");
}
}
#包括
#包括
#包括
#包括
#包括
无效休眠ms(uint32\u t ms){
结构timespects;
ts.tv_sec=0+(ms/1000);
ts.tv_nsec=1000*1000*(ms%1000);
奈米睡眠(&ts,空);
}
int main(int argc,char*argv[]){
长int cnt=0;
char buf[0x10]={0};
而(1){
sleepMs(100);
sprintf(buf,'%ld\n',++cnt);
如果(写入(标准输出文件号、buf、标准输出文件(buf))=-1)
佩罗(“书面”);
}
}
现在,第二个程序应该读取第一个程序的stdout(请记住,我最终希望使用进程进行读写,因此使用popen()是一个技术正确的解决方案)因为上面的用例可能在这个特定的情况下是正确的,因为我简化了我的实验,只捕获了底部程序的标准输出)。我期望从底层程序读取上层程序写入标准输出的任何数据。但它什么也看不到。原因在哪里?(第二项问题)
#包括
#包括
#包括
#包括
#包括
#包括
无效休眠ms(uint32\u t ms){
结构timespects;
ts.tv_sec=0+(ms/1000);
ts.tv_nsec=1000*1000*(ms%1000);
奈米睡眠(&ts,空);
}
int main(){
int pipe_fds[2];
int n;
char buf[0x100]={0};
pid_t pid;
管道(管道);
char*cmd[]={/path/to/program/over”,NULL};
如果((pid=fork())==0){/*child*/
dup2(pipe_fds[1],1);//将进程的stdout设置为管道的写入端
execvp(cmd[0],cmd);//执行程序。
fflush(stdout);
perror(cmd[0]);//仅在发生错误时到达
出口(0);
}否则,如果(pid=-1){/*失败*/
佩罗尔(“福克”);
出口(1);
}else{/*父项*/
而(1){
sleepMs(500);//稍等一下,让子程序运行一点
printf(“正在尝试读取\n”);
如果((n=read(pipe_fds[0],buf,0x100))>=0){//尝试从管道的读取端读取子进程的标准输出
buf[n]=0;/*终止字符串*/
fprintf(stderr,“get:%s”,buf);//这应该打印“12345678910…”
}否则{
fprintf(stderr,“读取失败\n”);
佩罗(“阅读”);
}
}
}
}
您的想法是正确的,我没有时间分析您的所有代码来指出具体的问题,但我确实想指出一些您在程序和终端如何工作方面可能忽略的事情
终端作为一个“文件”的概念是naivé。像vi这样的程序使用一个库(ncurses)来发送特殊的控制字符(并更改终端设备dri)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
void sleepMs(uint32_t ms) {
struct timespec ts;
ts.tv_sec = 0 + (ms / 1000);
ts.tv_nsec = 1000 * 1000 * (ms % 1000);
nanosleep(&ts, NULL);
}
int main() {
int pipe_fds[2];
int n;
char buf[0x100] = {0};
pid_t pid;
pipe(pipe_fds);
char *cmd[] = {"/path/to/program/above", NULL};
if ((pid = fork()) == 0) { /* child */
dup2(pipe_fds[1], 1); // set stdout of the process to the write end of the pipe
execvp(cmd[0], cmd); // execute the program.
fflush(stdout);
perror(cmd[0]); // only reached in case of error
exit(0);
} else if (pid == -1) { /* failed */
perror("fork");
exit(1);
} else { /* parent */
while (1) {
sleepMs(500); // Wait a bit to let the child program run a little
printf("Trying to read\n");
if ((n = read(pipe_fds[0], buf, 0x100)) >= 0) { // Try to read stdout of the child process from the read end of the pipe
buf[n] = 0; /* terminate the string */
fprintf(stderr, "Got: %s", buf); // this should print "1 2 3 4 5 6 7 8 9 10 ..."
} else {
fprintf(stderr, "read failed\n");
perror("read");
}
}
}
}