如何将 printf() 重定向到文件然后返回控制台

Kyl*_*nio 2 c io console printf file

我正在开发一个项目,该项目要求我将 C 程序上的迷你 shell 中的输出输出到文件。使用./program > file.txt不会起作用

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

我怎样才能在C中实现这一点?

Jon*_*ler 6

带有 fork 和 exec 的普通 shell

如果您有一个正常的 shell,您正在使用fork()andexecvp()或它的亲戚之一,那么您应该分叉子进程并在子进程中处理 I/O 重定向(使用open()dup2()ordup()close()),保持父进程的 I/O 不变。

假设您已将命令解析为:

char *argv[] = { "simple-command", "arg1", "arg2", 0 };
Run Code Online (Sandbox Code Playgroud)

和标准输出文件名:

char *file1 = "filename";
Run Code Online (Sandbox Code Playgroud)

然后:

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 */
}
Run Code Online (Sandbox Code Playgroud)

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

您还可以使用以下序列:

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...
Run Code Online (Sandbox Code Playgroud)

无需 fork 和 exec 即可重定向内置命令

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

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

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);
Run Code Online (Sandbox Code Playgroud)

工作代码

抱歉,我没有完全理解这段代码(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;
}
Run Code Online (Sandbox Code Playgroud)

样本输出

$ ./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.
$ 
Run Code Online (Sandbox Code Playgroud)