C 与子进程stdout/stdin通信

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设置为调用进程之一 因为这

我试图与一个进程通信(该进程本身会写入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");
                }
            }
        }
    }