popen()执行逻辑与sh -c cmdstring不符的原因排查
关于popen执行逻辑的误解分析
测试场景
我测试了《UNIX环境高级编程(第3版)》(APUE)15.3节中popen和pclose函数的示例代码,代码如下:
#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
我的推论哪里出错了?
错误原因分析
你的核心误解在于混淆了popen的执行逻辑和数据传递方式:
popen(PAGER, "w")执行的是sh -c "${PAGER:-more}",而非你以为的sh -c "${PAGER:-more} test sentence"。这里的cmdstring就是${PAGER:-more},并没有把文件内容作为命令参数传递给more。- 程序中通过
fputs写入fpout的内容,是通过管道传递给more的标准输入,而非作为命令行参数。当more没有接收到文件名参数时,默认会从标准输入读取内容,这就是程序能正常输出的原因。 - 你手动执行
${PAGER:-more} test sentence时,test和sentence被当作文件名参数,自然会报错;但程序里的more是从管道读数据,和直接传文件名是两种完全不同的输入方式。
简单来说,程序的逻辑是:打开文件→通过管道把文件内容传给more的标准输入→more读取标准输入并显示,而非把文件内容作为参数传给more。
内容的提问来源于stack exchange,提问作者JiangZiyang
相关产品推荐
相关产品推荐

