如何从 stderr 读取错误消息?

How can I read the error message from stderr?

我们正在研究 Pipes,我必须创建一个 C 代码来读取编译另一个 C 程序的错误消息。我正在使用 dup2 将 stederr 复制到管道的写入端,之后我使用 read 方法从管道的另一端读取,但在某些情况下我无法读取整个错误信息。 这是我正在尝试做的事情:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    const char *const cmd0 = argv[1];
    int fds_pair[2];
    pipe(fds_pair);
    
    pid_t pid;
    if ((pid = fork()) == 0) {

        close(fds_pair[0]);

        /// stderr <- pipe's write
        dup2(fds_pair[1], 2);
        execlp("g++", "g++", cmd0, "-o", "my_program", NULL);
        return 0;

    } else {
        close(fds_pair[1]);
    
        char expression[256];
        memset(expression, '[=10=]', sizeof(expression));
        size_t readret;            
        readret = read(fds_pair[0], &expression, sizeof(expression));
        printf("%s", expression);

        while (readret > 0) {
            readret = read(fds_pair[0], expression, sizeof(expression));
            printf("%s", expression);
        }
    }
    close(fds_pair[0]);
    return 0;
}

我面临的问题是,当我尝试获取程序错误时:

1   #include <stdio.h>
2
3   int main () {
4     ;float v_05778c89;
5     float v_ char v_ int v_ double v_ double v_
6   }

这是我从上面的代码中读到的错误消息:

main2.c: In function ‘int main()’:
main2.c:5:5: error: expected initializer before ‘float’
5 |     float v_
  |     ^~~~~

我没有收到错误:expected '=', ',', ';', 'asm' or 'attribute' before ' }' 令牌 6 | }

我对自己做错了什么或必须解决的问题感到困惑

您期望的错误消息 (expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘}’ token 6 | }) 是典型的 MS Visual C++,而不是 GNU g++。这可能是主要问题——不切实际的期望。

我修改了您的代码以包含我的版本中的第一段代码,如下所示,以便使用 argc 并通过我的默认编译选项。 当我运行你修改后的代码(源文件re23.c编译成re23),我得到输出:

$ re23 main2.c
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
$

该消息出现两次,因为您没有正确处理来自子进程(C++ 编译器)的 I/O。基本上,您需要在打印任何内容之前读取数据并检查它是否有效。

编译(GCC 11.2.0 运行ning on macOS Big Sur 11.6.4):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common re23.c -o re23
$

我也在评论中提到了各种问题。我相信它们都已固定在下面的代码中,根据我的标准可以正常工作。它与您的相似,并且与您的相似,但有一些关键差异。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s sourcefile.cpp\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    const char *const cmd0 = argv[1];
    int fds_pair[2];
    if (pipe(fds_pair) < 0)
    {
        fprintf(stderr, "%s: failed to create pipe: %d %s\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "%s: failed to fork: %d %s\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        dup2(fds_pair[1], STDERR_FILENO);
        close(fds_pair[0]);
        close(fds_pair[1]);
        execlp("g++", "g++", cmd0, "-o", "my_program", NULL);
        fprintf(stderr, "%s: failed to exec '%s': %d %s\n",
                argv[0], "g++", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else
    {
        close(fds_pair[1]);
        char expression[256];
        ssize_t readret;
        while ((readret = read(fds_pair[0], &expression, sizeof(expression))) > 0)
        {
            printf("%.*s", (int)readret, expression);
        }
        close(fds_pair[0]);
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("%s: child %d exited with status 0x%.4X\n", argv[0], corpse, status);
    }
    return 0;
}

这里有一些示例 运行s — 我使用源代码 re37.c 作为上面的源代码,创建程序 re37:

$ ./re37
Usage: ./re37 sourcefile.cpp
$ ./re37 nonexistent.cpp
cc1plus: fatal error: nonexistent.cpp: No such file or directory
compilation terminated.
./re37: child 4644 exited with status 0x0100
$ cat main2.c
#include <stdio.h>

int main () {
  ;float v_05778c89;
  float v_ char v_ int v_ double v_ double v_
}
$ ./re37 main2.c
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
./re37: child 4670 exited with status 0x0100
$