如何将system()的输出重定向到文件?

7
在这个 C 程序中,
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int file = open("Result", O_CREAT|O_WRONLY, S_IRWXU);

    dup2(stdout, file);
    system("ls -l");

    return 0;
}

我正在尝试将system()的输出重定向到一个文件,为此我使用了dup2,但是它没有起作用。
这段代码有什么问题?
请告诉我是否有更好的方法来做到这一点(不使用终端上的>)。

3
为什么不直接在system命令中使用>重定向符号? - Alvin Wong
使用 system("ls -l > Result"); 或者自己编写 fork()/exec*() 组合。 - Hristo Iliev
1
不要使用 system,这总是错误的。自己运行子进程而不使用 shell,可以使用 forkexecvpposix_spawn - R.. GitHub STOP HELPING ICE
@R.. 在这里,“总是错误”是指system()函数总是错误的,还是使用system()函数是错误的。请澄清一下。 - Eight
我是说 system 应该被视为已弃用,因为 (1) 很难对它进行任何有用的操作,而且 (2) 尝试对其进行任何有用的操作(例如传递参数)几乎肯定会创建与 shell 转义相关的危险(可能涉及安全问题)的错误。 - R.. GitHub STOP HELPING ICE
5个回答

7

stdout是标准输出流的FILE *指针。而dup2函数期望文件描述符,你也搞混了参数的顺序。 请使用:

dup2(file, 1);

相反地。

关于更好的做法部分。这种方法不好,因为在 system 调用完成后,您可能希望恢复标准输出。您可以通过多种方式实现这一点。您可以将其 dup 到某个位置,然后 dup2 回来(并关闭 dup)。我个人不喜欢按照其他答案中建议的编写自己的 cat 实现。如果您想要的唯一一件事是将单个 shell 命令与 system 重定向到文件系统中的文件,则最直接且简单的方法可能是构造执行此操作的 shell 命令,如下所示:

system("ls -l > Result");

但是如果文件名(Result)来自用户输入,您必须小心,因为用户可以提供像'Result; rm -rf /*'这样的文件名。

此外,在安全方面,建议在评论中指定ls的完整路径:

system("/bin/ls -l > Result");

1
我编辑了你的帖子,将ls的绝对路径包含在你的示例中。 - Wug
撤销编辑,因为 OP 运行了 "ls",这会改变系统调用的含义。问题也与绝对路径或相对路径无关。我认为最好询问 @unkulunkulu 是否愿意修改他的答案。 - Andomar
@Andomar,实际上,我接受了这个编辑,但无论如何,你的两点都是正确的,我没有看到任何区别 :) Wug,感谢您的建议,它将在评论中被看到。 - unkulunkulu
@unkulunkulu:如果你同意Wug的观点,请毫不犹豫地撤销我的更改。您可以通过单击“X分钟前编辑”并选择适当的修订版本中的“还原”来执行此操作。 - Andomar
1
调用系统而不指定绝对路径通常是一个非常糟糕的想法,因为一些心怀不良的人可能通过玩弄PATH破坏您的程序。这对于初学者来说不是一个好习惯。因此,这就是我修改的原因。 - Wug

5
简单的做法是确实使用>:
#include <stdio.h>
int main()
{
    system("ls -l > /some/file");

    return 0;
}

另一种选择是使用popen(),大致如下:

   #include <stdio.h>
   #include <stdlib.h>
   main()
   {
           char *cmd = "ls -l";
           char buf[BUFSIZ];
           FILE *ptr, *file;

           file = fopen("/some/file", "w");
           if (!file) abort();
           if ((ptr = popen(cmd, "r")) != NULL) {
                   while (fgets(buf, BUFSIZ, ptr) != NULL)
                           fprintf(file, "%s", buf);
                   pclose(ptr);
           }
           fclose(file);
           return 0;
   }

4
你应该使用popen()库函数,从返回的FILE *中读取数据块,并将它们写入你喜欢的任何输出文件。
参考资料: 链接

0

使用dup代替dup2dup创建一个别名文件描述符,其值应始终为最小可用文件描述符。

new_fd = dup(file); - 在此语句中,file可能具有值3(因为stdin0stdout1stderr2)。 因此,new_fd将是4

如果您想将stdout重定向到文件,则请按以下方式操作。

close(stdout);
new_fd = dup(file);

现在,dup将返回1作为file描述符的别名,因为我们关闭了stdout,所以打开的文件描述符是0,2,3,而1是最小可用的文件描述符。

如果您正在使用dup2,意味着dup2(file,1); - 就像这样


使用 dup() 而不是 dup2() 只有在您不关心副本放置位置时才这样做。如果您关心,请使用 dup2() -- 这是它的显式目的!如果您只是希望一切顺利,就像这个答案所做的那样,它会正常工作,直到某一天模式被打破,您将不知道为什么它失败了。这些都会导致一些令人恼火的深夜调试会话,而这些本可以通过从一开始就正确地执行来避免。 - mah

0
其他答案都是有效的,但由于您询问了一种在终端中不使用>运算符重定向system()输出的方法,我建议使用FILE *freopen(const char *filename, const char *mode, FILE *stream)函数(它是stdio.h头文件的一部分)。在我看来,这是最能代表>运算符的方式。
您的代码可能变成以下形式:
# include <stdio.h>
# include <stdlib.h>

int main(void){
   
   char line[1024];
   FILE *handle = freopen("result.txt", "w", stdout);
   if(!handle)  
     exit(EXIT_FAILURE); //file didn't open
   
   sprintf(line, "ls -l");
   system(line);
   return 0;
}

我建议先将命令放入缓冲区,然后再调用system()。这样做可以简化需要使用不同参数执行相同命令的情况。

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