popen() 的输出与执行“sh -c cmdstring”不同

问题描述 投票:0回答:1

描述

我测试了APUE(UNIX环境中的高级编程,3ed)、15.3 popen和plcose函数的示例。
示例代码如下

#include "apue.h"
#include <sys/wait.h>

#define PAGER   "${PAGER:-more}" /* environment variable, or default */

int
main(int argc, char *argv[])
{
    char    line[MAXLINE];
    FILE    *fpin, *fpout;

    if (argc != 2)
        err_quit("usage: a.out <pathname>");
    if ((fpin = fopen(argv[1], "r")) == NULL)
        err_sys("can't open %s", argv[1]);

    if ((fpout = popen(PAGER, "w")) == NULL)
        err_sys("popen error");

    /* copy argv[1] to pager */
    while (fgets(line, MAXLINE, fpin) != NULL) {
        if (fputs(line, fpout) == EOF)
            err_sys("fputs error to pipe");
    }
    if (ferror(fpin))
        err_sys("fgets error");
    if (pclose(fpout) == -1)
        err_sys("pclose error");

    exit(0);
}

我的输入文件是“temp.in”,我们有

$ cat temp.in
test sentence

示例的输出如下

$ ./popen2 temp.in 
test sentence

问题

如 APUE 中所示,执行

fpout = popen(cmdstring, "w")
类似于其子进程执行
sh -c cmdstring
。因此,上一节中显示的代码
popen
应该执行

sh -c "${PAGER:-more} test sentence"

我的操作系统(Ubuntu 22.04.3 LTS)没有名为

PAGER
的环境值,因此它应该执行

sh -c "more test sentence"

但是,据我所知,命令

more
的选项应该是文件名。在我的系统中测试以下顺序并获得输出,这与我上面推断的不同:

$ ${PAGER:-more} temp.in 
test sentence
$ ${PAGER:-more} test sentence
more: can't open test: no such file or directory
more: can't open sentence: no such file or directory

我的推论有什么问题吗?

c unix pipe
1个回答
0
投票

好了,

popen()
的逐步工作原理如下(我们忽略一些不相关的检查):
第一:货币进程调用
pipe()
创建管道

int pfd[2];  
pipe(pfd);

第二:当前进程调用

fork()
创建子进程。

if (fork() == 0)
    /* child process work */
else if(fork() > 0)
    /* parent process work */

第三:子进程和父进程关闭自己的fileno以创建单向管道。编辑

popen(cmdstring, "r")
,父进程关闭其写文件号,子进程关闭其读文件号。

/* child process work */
    close(pfd[0]);  // pfd[0] is read port
/* parent process work */
    close(pfd[1]);  // pfd[0] is write port

第四:父进程和chile进程重定向自己的fileno。编辑

popen(cmdstring, "r")
,父进程将其
STDIN_FILENO
重定向至读端口,子进程将其
STDOUT_FILENO
重定向至写端口。

/* child process work */
    dup2(pfd[1], STDOUT_FILENO);  
    close(pfd[1]);  
/* parent process work */
    dup2(pfd[0], STDIN_FILENO);  
    close(pfd[0]);

然后,子进程可以将其输出写入管道,父进程可以通过其标准输入从其子进程读取消息。

回到问题。

popen({PAGER:-more}, "w")
将货币进程的标准输出连接到
more
进程的标准输入。
因此,
fgets(line, MAXLINE, fpin)
argv[1]
读取字符串并将字符串通过管道传输到more,more将其打印到终止。例子的效果类似

cat $argv1 | ${PAGER:-more}
© www.soinside.com 2019 - 2024. All rights reserved.