我正在开发一个项目,要求我在C程序的迷你shell中输出输出到文件。 使用./program > file.txt
无效。
我有一个运行小命令的迷你shell程序,我想在命令结尾处有> filename
的情况下这样做,它会将所有文本从printf()
重定向到一个文件而不是控制台,然后将其重定向回控制台。
如何在 C 中完成此操作?
答案 0 :(得分:4)
如果你有一个正常的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...
除了在错误的过程中重定向,使用
freopen()
或者fdopen()
并重新开启/dev/stdout
将无效。 /dev/stdout
指向的文件在程序运行时发生更改,文件在主程序更改其文件描述符1(标准输出)指向的位置时打开或关闭。
如果您真的需要重定向主shell的标准输出以用于内置命令,而不使用fork()
和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);
对不起,我还没有完全理解这段代码(C新手)。你能举出一个完整的例子吗?
行。我已经放弃了我第一次编码的内置命令的函数指针的通用性;我总结说它可能会让你更加困惑。我拒绝在没有“打印错误”功能的情况下(尽管我经常通过errno
报告strerror()
以及打印基本错误消息)。
我调用了源文件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.
$
答案 1 :(得分:2)
您可以使用freopen。
freopen("desiredFileName", "w", stdout);
完成后,您可以撤消重新分配:
freopen("/dev/stdout", "w", stdout);
在Unix / Linux系统上。我不确定是否有可靠的方法可以恢复。
<强>更新强>
如果可能,您可以使用fprintf
代替printf
,根据需要提供stdout
或所需文件的句柄。