如何将System命令的stderr重定向到stdout或文件中?

10

在使用 shell 命令 $ avrdude -c usbtiny 时,会将文本输出到 stderr。由于它不是 stdout,我不能通过像 head-less-more 等命令来读取它。我希望将文本输出到 stdout 或文件中。在 C 中如何实现?我已经尝试过通过我的上一个问题来解决这个问题,但仍未解决。

4个回答

24

我没有在OpenBSD中尝试过这样的操作,但在至少几个*nix系统中,您可以使用dup2来实现此操作。

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

int main(void) {  

  fprintf(stderr, "This goes to stderr\n");

  dup2(1, 2);  //redirects stderr to stdout below this line.

  fprintf(stderr, "This goes to stdout\n");
}

有另外两个类似的答案,但出于某种原因,它们似乎认为您需要先进行分叉才能使用dup2。 - JeremyP

3

通常的方法可能是这样的:

avrdude -c usbtiny 2>&1

这会将本应输出到stderr的内容重新指向输出到stdout。如果您想将其重定向到文件,则可以执行以下操作:

avrdude -c usbtiny 2> outputfile.txt

@user355926:在哪个平台上?标准并没有涵盖这一点,所以你需要一些非可移植的代码来在C语言中实现它。 - Jerry Coffin
我需要以某种方式阻止C中的命令,以便我可以获取其stderr。 - otto

0
以下使用POSIX函数将标准输出文件号复制到标准错误文件号。在POSIX页面中,将stderr复制到stdout作为该函数的示例用法给出了dup2
#include <unistd.h>
#include <stdio.h>

int main (void)
{
    pid_t child = fork();

    if (child == 0)
    {
        dup2(STDOUT_FILENO, STDERR_FILENO);
        execlp("avrdude", "-c", "usbtiny", NULL);
    }
    else if (child > 0)
    {
        waitpid(child);
        puts("Done!");
    }
    else
    {
        puts("Error in forking :(");
    }

    return 0;
}

0

我需要以某种方式阻止C语言命令,以便我可以获得它的stderr。

首先阅读man forkman exec了解如何启动子进程。查看man 7 signalman sigactionman wait了解如何回收子进程。

最后,查看man dup2

未经测试的代码示例:

int pip_stderr[2];
int r;
int pid;

r = pipe(pip_stderr);
assert( r != -1 );

int pid = fork();
assert( pid != -1 );
if (pid == 0) { /* child */
   /* child doesn't need to read from stderr */
   r = close(pip_stderr[0]); assert( r != -1 );
   /* make fd 2 to be the writing end of the pipe */
   r = dup2(pip_stderr[1], 2); assert( r != -1 );
   /* close the now redundant writing end of the pipe */
   r = close(pip_stderr[1]); assert( r != -1 );
   /* fire! */
   exec( /* whatever */ );
   assert( !"exec failed!" );
} else { /* parent */
   /* must: close writing end of the pipe */
   r = close( pip_stderr[1] ); assert( r != -1 );

   /* here read from the pip_stderr[0] */

   r = waitpid(pid,0,0); assert( r == pid );
}

使用dup2()函数,我们将子进程的stderr(即文件描述符2)替换为管道的写入端。在fork()之前调用pipe()函数。在fork()之后,我们还必须关闭管道的所有“悬空”端口,以便父进程中的读取实际上会收到EOF。

可能有一个更简单的解决方案,可以使用stdio,但我不知道。由于popen()通过shell运行命令,可能可以告诉它将stderr重定向到stdout(并将stdout发送到/dev/null)。从未尝试过。

还可以使用mktemp()函数(man 3 mktemp)创建临时文件名,组合system()命令以将命令的stderr重定向到临时文件,并在system()返回后读取临时文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接