运行 无效命令时未捕获 STDERR 输出

STDERR output is not being captured when running invalid command

我正在尝试捕获 运行 命令 /bin/lsx -lah / 的输出。

输出应该是:bash: /bin/lsx: no such file or directory

但是,我在 printf().

中没有得到任何东西

这是代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);

int main() {
  int link[2];
  pid_t pid;
  char foo[4096];

  if (pipe(link)==-1)
    die("pipe");

  if ((pid = fork()) == -1)
    die("fork");

  if(pid == 0) {

    dup2 (link[1], STDOUT_FILENO);
    close(link[0]);
    close(link[1]);
    execl("/bin/lsx", "ls", "-1", (char *)0);
    die("execl");

  } else {

    close(link[1]);
    int nbytes = read(link[0], foo, sizeof(foo));
    printf("Output: (%.*s)\n", nbytes, foo);
    wait(NULL);

  }
  return 0;
}

我只是想知道为什么输出没有被捕获并打印在底部 printf()

正如 John 所指出的,您只是捕获 stdout,而不是 stderr,并且 well-formed 程序通常会将错误消息发送到 stderrdie 你展示的宏就是一个例子)。

一个快速的解决方案是通过再次调用 dup2

将子进程的 stderr 重定向到它的 stdout
dup2(link[1], STDOUT_FILENO);
dup2(STDOUT_FILENO, STDERR_FILENO);

这会将两个输出流重定向到单个管道。

或者,用两个管道捕获两个流以在它们传播时保持它们的分离。

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

#define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);

ssize_t push_through(const char *prefix,
        FILE *out, int in,
        char *buf, size_t bufsz)
{
    ssize_t bytes = read(in, buf, bufsz);

    if (bytes > 0)
        fprintf(out, "%s: (%.*s)\n", prefix, (int) bytes, buf);

    return bytes;
}

int main(void)
{
    pid_t pid;
    int stdout_pipe[2];
    int stderr_pipe[2];
    char buffer[4096];

    if (pipe(stdout_pipe) == -1 || pipe(stderr_pipe) == -1)
        die("pipe");

    if ((pid = fork()) == -1)
        die("fork");

    if (pid == 0) {
        close(stdout_pipe[0]);
        close(stderr_pipe[0]);

        dup2(stdout_pipe[1], STDOUT_FILENO);
        dup2(stderr_pipe[1], STDERR_FILENO);

        close(stdout_pipe[1]);
        close(stderr_pipe[1]);

        execl("/bin/lsx", "ls", "-1", (char *) NULL);
        perror("execl failed because");
        die("execl");
    } else {
        ssize_t outbytes, errbytes;

        close(stdout_pipe[1]);
        close(stderr_pipe[1]);

        do {
            outbytes = push_through("out", stdout, stdout_pipe[0],
                    buffer, sizeof buffer);
            errbytes = push_through("err", stderr, stderr_pipe[0],
                    buffer, sizeof buffer);
        } while (outbytes > 0 || errbytes > 0);

        wait(NULL);
    }
}

如果 execl 失败,调用 perror 打印更详细的错误消息可能会很有用。

注意预期错误

bash: /bin/lsx: no such file or directory

可能被误导了,因为 exec* 函数仅在特定条件下遵从 shell。

man 3 exec 中唯一提到的 shell 是关于 execlpexecvpexecvpe:

If the header of a file isn't recognized (the attempted execve(2) failed with the error ENOEXEC), these functions will execute the shell (/bin/sh) with the path of the file as its first argument. (If this attempt fails, no further searching is done.)