popen和pclose,简化与子进程的管道通信

Last Updated: 2023-05-24 09:15:03 Wednesday

-- TOC --

在Linux环境下创建一个子进程,通过pipe的方式与之通信,基本流程是:创建pipe;fork;dup2;exec;wait。

这套流程有些复杂,glibc提供popen和pclose这两个接口,可以在某些场景下替代这个复杂流程。

// popen, pclose - pipe stream to or from a process
// man 3 popen
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

pipe,不管是否命名,都是单向的。popen接口的type只能填"r",或者"w"。如果是读,popen创建的pipe连接子进程的stdout,read from。如果是写,连接子进程的stdin,write to。glibc2.9之后,type还可以是"e",效果是给pipe设置FD_CLOEXEC。

The command argument is a pointer to a null-terminated string containing a shell command line. This command is passed to /bin/sh using the -c flag; interpretation, if any, is performed by the shell.

从上面接口申明可以看出,popen返回一个普通标准的FILE指针,因此很多C标准库中的f开头的接口就都可以使用了(而单纯的pipe接口,返回的是fd的int值,没有封装),除了fclose,popen的子进程,必须要调用pclose来关闭。pclose内部会调用wait,避免僵尸进程,同时返回子进程的exit code。

下面是一段测试代码:

#include <stdio.h>


int main(void) {
    FILE *pf;

    if ((pf=popen("python3 -c 'import os;print(os.environ)'","r")) == NULL) {
        fprintf(stderr, "popen error.\n");
        return 1;
    }

    int c;
    while ((c=fgetc(pf)) != EOF)
        printf("%c", (unsigned char)c);

    pclose(pf);
    return 0;
}

下面是用"w"的方式使用popen接口的示例:

#include <stdio.h>


int main(void) {
    FILE *pf;
    char cmd[] = "python3 -c 'import sys; \
                              print(\"##\",sys.stdin.read())'";

    if ((pf=popen(cmd,"w")) == NULL) {
        fprintf(stderr, "popen error.\n");
        return 1;
    }

    fprintf(pf, "%s\n", "abcdefg3123123123");
    fprintf(pf, "%d", 123123);

    pclose(pf);
    return 0;
}

所有通过fprintf写到pipe中的数据,会被sys.stdin.read一口气读出来。

Python的subprocess.Popen,默认shell=False!

本文链接:https://cs.pynote.net/sf/linux/prog/202207201/

-- EOF --

-- MORE --