我试图使用popen()获得子进程的存在状态.

Case 1:调用函数,shell命令返回错误.这是预期的工作.

func("du -sh _invalid_file_");

Output:

du: cannot access '_invalid_file_': No such file or directory
Child exit value: 1

在这里,子存在状态与bash中运行的退出值du相同.

$ du -sh _invalid_file_
du: cannot access '_invalid_file_': No such file or directory
$ 
$ echo $?
 1
$

Case 2: (Error case)使用下面的shell命令调用函数返回成功.

func("du -sh");

Output:

Child exit value: 141

请建议解决此问题.将代码粘贴到下面.

int func(char *cmd)
{
    FILE *pfp = NULL;
    int retval, status;

    pfp = popen(cmd, "r");
    if (pfp == NULL) {
        perror("popen failed");
        exit(1);
    }

    status = pclose(pfp);
    if (status < 0) {
        perror("pclose error");
    } else {
        if (WIFEXITED(status)) {
            retval = WEXITSTATUS(status);
            printf("Child exit value: %d\n", retval);
        }
    }

    return 0;
}

推荐答案

您在实际读取任何数据之前关闭了管道.这将导致子进程接收SIGPIPE(13)并被终止.

popen()将使用/bin/sh -c来运行命令,因此实际上是sh -c "du ..."在运行.当duSIGPIPE(13)杀死时,sh将退出状态设置为128+13=141,并且由于du是shell会话中的最后一个命令,因此141将是sh -c ...命令的最终退出状态,得到141.

要修复它,您需要在pclose()之前从管道中读取数据.

int func(char *cmd)
{
    FILE *pfp = NULL;
    char buf[1024];
    int retval, status;

    pfp = popen(cmd, "r");
    if (pfp == NULL) {
        perror("popen failed");
        exit(1);
    }

    /* read from the pipe or close() may cause the process to receive SIGPIPE */
    while (fgets(buf, sizeof buf, pfp) ) {
        printf("%s", buf);
    }

    status = pclose(pfp);
    if (status < 0) {
        perror("pclose error");
    } else {
        if (WIFEXITED(status)) {
            retval = WEXITSTATUS(status);
            printf("Child exit value: %d\n", retval);
        }
    }

    return 0;
}

以下内容来自man 7 signal:

   Signal     Value     Action   Comment
   ----------------------------------------------------------------------
   ... ...

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers; see pipe(7)
   ... ...

SIGPIPE种行为每天都在发生,尽管并不明显.您可以这样try :

$ od /dev/urandom | head -n1
0000000 067255 052464 166240 113163 122024 054232 015444 110641
$ declare -p PIPESTATUS    # see PIPESTATUS in `man bash'
declare -a PIPESTATUS=([0]="141" [1]="0")
$
$ { od /dev/urandom; echo EXIT=$? >&2; } | head -n1
0000000 020553 063350 025451 116300 006505 151644 122151 175763
EXIT=141

关于shell的128+signal行为,以下内容来自man bash(Bash与sh兼容,因此这也适用于sh):

当命令在致命信号N上终止时,bash使用值128+N作为退出状态.

C++相关问答推荐

当我try 计算一个多项时,看到segfault.我做错了什么?

sizeof结果是否依赖于字符串的声明?

在使用GTK 4 Columnview列表模型时,如何为多列添加排序函数.C编码,Linux/GNOME环境

ESP32在vTaskDelay上崩溃

我无法让LLDB正确运行我的可执行文件

如何将字符串传递给函数并返回在C中更改的相同字符串?

ATmega328P EEPROM未写入

struct 上的OpenMP缩减

如何使用[BTStack]BLE发送大型(>;2kb)信息包

整型文字后缀在左移中的用途

为什么指针运算会产生错误的结果?

用C语言计算文本文件中的整数个数

如何按顺序将所有CSV文件数据读入 struct 数组?

获取前2个连续1比特的索引的有效方法

C语言中的数字指针

从BIOS(8086)中读取刻度需要多少?

`预期说明符-限定符-列表在‘(三元运算符中的’token`‘之前

从C中的函数返回静态字符串是不是一种糟糕的做法?

如何使用空元素块声明指针数组

为什么这个代码的最后一次迭代不能正常工作?