【问题标题】:How to redirect printf() to file and then back to console如何将 printf() 重定向到文件然后返回到控制台
【发布时间】:2014-12-13 14:13:16
【问题描述】:

我正在做一个项目,该项目要求我在我的 C 程序的 mini shell 中进行输出以输出到文件。 使用./program > file.txt 将不起作用

我有一个运行小命令的迷你 shell 程序,我想这样当有人在命令末尾有一个 > filename 时,它会将所有文本从 printf() 重定向到一个文件,而不是控制台,然后将其重定向回控制台。

如何在 C 中完成此操作?

【问题讨论】:

  • fork(), dup2(), execvp() -- 上下文中的关键之一是dup2(),它只需要在fork() 之后,所以父母不必费兹有自己的stdout
  • @BLUEPIXY:使用freopen() 可以获取文件;之后你如何切换回来?至少除了打电话给freopen()之外,还有更多事情要做。
  • @JonathanLeffler 你能举个例子吗?我之前尝试过使用 fork() 和 dup2(),但它似乎不起作用。

标签: c file io console printf


【解决方案1】:

带有 fork 和 exec 的普通 shell

如果你有一个普通的 shell,你正在使用 fork()execvp() 或其亲属之一,那么你应该分叉子进程并在子进程中处理 I/O 重定向(使用 open()dup2()dup(),和 close()),保持父级的 I/O 不变。

假设您已将命令解析为:

char *argv[] = { "simple-command", "arg1", "arg2", 0 };

并将标准输出文件名改成:

char *file1 = "filename";

然后:

if ((pid = fork()) < 0)
    ...report error...
else if (pid == 0)
{
    int fd = open(file1, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0)
        ...report error and exit...
    dup2(fd, STDOUT_FILENO);
    close(fd);
    execvp(argv[0], argv);
    ...report error and exit...
}
else
{
    /* Do whatever the parent needs to do */
}

请注意,父级根本不需要更改其标准输出。

你也可以使用序列:

close(STDOUT_FILENO);
int fd = open(file1, O_WRONLY | O_CREAT | O_TRUNC, 0644); // Or 0666
if (fd < 0)
    ...report error and exit...
if (fd != STDOUT_FILENO)
    ...standard input must have been closed too...

不使用 fork 和 exec 重定向内置命令

除了在错误的过程中重定向之外,使用 freopen() 或者 fdopen() 并重新打开 /dev/stdout 将不起作用。 /dev/stdout 指向的文件会随着程序的运行而变化,并且随着主程序更改其文件描述符 1(标准输出)指向的内容而打开或关闭文件。

如果您确实需要将主 shell 的标准输出重定向为内置,其中不使用 fork()exec()-family 函数,那么您需要执行以下等效操作:

fflush(stdout);  // Safety precaution
int fd1 = open(file1, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd1 < 0)
    ...report error and do not continue...
int fd2 = dup(STDOUT_FILENO);
if (fd2 < 0)
    ...report error and do not continue...
if (dup2(fd2, STDOUT_FILENO) < 0)
    ...report error and do not continue...
close(fd1);  // check for error?
...do whatever operations need standard output redirected...
fflush(stdout);  // Safety precaution
if (dup2(fd1, STDOUT_FILENO) < 0)  // Bug fixed!
    ...report error and do not continue...
close(fd2);

工作代码

对不起,我没有完全理解这段代码(C 的新手)。能举个完整的例子吗?

好的。对于我第一次编码的内置命令,我已经放弃了指向函数的指针的一般性;我的结论是它可能会让你更加困惑。我拒绝没有“打印错误”功能(尽管我经常加强它以报告errnostrerror() 以及打印基本错误消息)。

我将源文件称为simpleshell.c 和程序,因此称为simpleshell,但这夸大了它的作用。

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

static int err_report(const char *fmt, ...);

static int redirect_echo_to_file(char **argv, char *file)
{
    /* Connect standard output to given file */
    fflush(stdout);
    int fd1 = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd1 < 0)
        return err_report("Failed to open %s for writing\n", file);
    int fd2 = dup(STDOUT_FILENO);
    if (fd2 < 0)
        return err_report("Failed to duplicate standard output\n");
    if (dup2(fd1, STDOUT_FILENO) < 0)
        return err_report("Failed to duplicate %s to standard output\n", file);
    close(fd1);

    /* Write to standard output */
    char *pad = "";
    while (*argv != 0)
    {
        printf("%s%s", pad, *argv++);
        pad = " ";
    }
    putchar('\n');

    /* Reconnect original standard output */
    fflush(stdout);
    if (dup2(fd2, STDOUT_FILENO) < 0)
        return err_report("Failed to reinstate standard output\n");
    close(fd2);
    return 0;
}

int main(void)
{
    char *file1 = "file.01";
    char *file2 = "file.02";
    char *arguments[] = { "Hello", "world!", "This", "is your", "echo", "speaking." };
    printf("This is the surrogate shell at work\n");
    printf("Echo the same message to two different files (%s and %s)\n", file1, file2);
    if (redirect_echo_to_file(arguments, file1) != 0 ||
        redirect_echo_to_file(arguments, file2) != 0)
        return -1;
    printf("Regular shell output on standard output.\n");
    printf("Please check %s and %s for a special message.\n", file1, file2);
    return 0;
}

static int err_report(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    return -1;
}

样本输出

$ ./simpleshell
This is the surrogate shell at work
Echo the same message to two different files (file.01 and file.02)
Regular shell output on standard output.
Please check file.01 and file.02 for a special message.
$ cat file.01
Hello world! This is your echo speaking.
$ cat file.02
Hello world! This is your echo speaking.
$ 

【讨论】:

  • 您能解释一下execvp(argv[0], argv); 的用途吗?
  • 运行命令...你说,你有一个运行小命令的 shell,你需要能够运行一个小命令,它的输出到一个文件,而父进程继续在其他地方写。如果您的意思是“处理带有 I/O 重定向但不执行外部进程的内置命令”,那么您需要这么说。
  • 我收到了一堆错误,比如warning: implicit declaration of function ‘fork’ 另外,它运行的是自己的命令,而不是“真正的”linux 命令。
  • 好的;所以它不是一个普通的外壳。请参阅我的答案的修正。很高兴您收到fork() 的警告并注意警告;谢谢! fork() 的相关标头是 &lt;unistd.h&gt;;同上dup() 等。但是,open() 需要 &lt;fcntl.h&gt;
  • 对不起,我没有完全理解这段代码(C 的新手)。能举个完整的例子吗?
【解决方案2】:

您可以使用freopen

freopen("desiredFileName", "w", stdout);

完成后,您可以像这样撤消重新分配:

freopen("/dev/stdout", "w", stdout);

在 Unix/Linux 系统上。我不确定是否有可移植的方式来恢复。

更新

如果可能,您可以使用fprintf 代替printf,根据需要提供stdout 或所需文件的句柄。

【讨论】:

  • 使用freopen() 可以获取文件;之后你如何切换回来?至少除了打电话给freopen()之外,还有更多的事情要做。
  • 不错的尝试;但是当您第二次调用freopen() 时,/dev/stdout 指向的是文件...
  • @JonathanLeffler:/dev/stdout 是系统上的一个设备。 C 引用为stdout 的文件句柄已指向该文件。
  • /dev/ttyconcon: 用于Windows
  • 去试试吧...我想你最终会需要dup()dup2(),然后你可以在/dev/fd/N上使用freopen(),其中N是你复制的值stdout 首先。 fileno()fdopen() 也可能相关。但我认为这个问题没有抓住重点——需要处理的是子进程。在纯 (POSIX) shell 中,您可以使用:{ exec 3&gt;&amp;1 &gt;desiredFileName; execute commands...; exec 1&gt;&amp;3 3&gt;&amp;-; }
猜你喜欢
  • 2015-04-20
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-03
相关资源
最近更新 更多