【问题标题】:Redirect stdout to a file将标准输出重定向到文件
【发布时间】:2015-05-23 03:01:33
【问题描述】:

我正在尝试在 C 中执行相当于 bash 命令 ls>foo.txt 的操作。

下面的代码将输出重定向到一个变量。

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

int main(){
  int pfds[2];
  char buf[30];

  pipe(pfds);

  if (!fork()) {
    close(pfds[0]);
     //close(1);//Close stdout
    //dup(pfds[1]);
    //execlp("ls", "ls", NULL);
    write(pfds[1], "test", 5); //Writing in the pipe
    exit(0);
  } else {
    close(pfds[1]);  
    read(pfds[0], buf, 5); //Read from pipe
    wait(NULL);
  }
  return 0;
}

cmets 行指的是我认为重定向所需的那些操作。 我应该改变什么来将 ls 的输出重定向到 foo.txt?

【问题讨论】:

  • 当你模拟的 shell 语法中没有显示管道时,为什么要创建管道?
  • “等效于 C 中的 [this bash code]”不会成为 bash 问题。如果您希望 bash(而不是 C!)方面的专家查看它,请仅将其标记为 bash。

标签: c redirect pipe posix


【解决方案1】:

在处理将输出重定向到文件时,您可以使用freopen().

假设您正在尝试将您的 stdout 重定向到文件“output.txt”,那么您可以编写 -

freopen("output.txt", "a+", stdout); 

此处“a+”用于附加模式。如果文件存在,则文件以附加模式打开。否则会创建一个新文件。

使用freopen() 重新打开stdout 后,所有输出语句(printf、putchar)都被重定向到“output.txt”。因此,此后任何printf() 语句都会将其输出重定向到'output.txt' 文件。

如果您想再次恢复printf() 的默认行为(即在终端/命令提示符下打印),那么您必须使用以下代码再次重新分配stdout-

freopen("/dev/tty", "w", stdout); /*for gcc, ubuntu*/  

或者——

freopen("CON", "w", stdout); /*Mingw C++; Windows*/ 

但类似的技术适用于“stdin”。

【讨论】:

  • 我正在使用fork() 生成的进程上通过Linux 和FreeBSD 对其进行测试,并创建了output.txt 文件,但没有收到任何文本。这应该在这些情况下工作吗?或者也许在生成的进程上没有stdout 流?也许需要某种重定向?
【解决方案2】:

您的代码本质上是打开一个管道,然后分叉该进程并在子进程中(在注释代码中)关闭 stdout,将管道复制到 stdout 并执行和 ls 命令,然后(在非注释中代码)向管道写入 4 个字节。在父进程中,您从管道中读取数据并等待子进程完成。

现在您想将标准输出重定向到一个文件。您可以通过使用 open() 系统调用打开文件,然后将该文件描述符复制到标准输出来做到这一点。类似的东西(我还没有测试过,所以要小心代码中的错误):

int filefd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
if (!fork()) {
  close(1);//Close stdout
  dup(filefd);
  execlp("ls", "ls", NULL);
} else {
  close(filefd);
  wait(NULL);
}
return 0;

但是,您也可以按照其他答案的建议使用 freopen。

但是,我对您的代码和我修改后的代码有几个担忧:

  • pipe() 和 open() 系统调用可能会失败。您应该始终检查系统调用失败。

  • fork() 系统调用可能会失败。同上。

  • dup2() 可以用来代替 dup();否则,如果标准输入未打开,代码将失败,因为它会复制到第一个可用的文件描述符。

  • execlp() 系统调用可能会失败。同上。

  • 我认为 wait() 可以被信号 (EINTR) 中断。如果系统调用被信号中止 (errno == EINTR),建议将其包装在重试系统调用的包装器中。

【讨论】: