如果您有一个正常的 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)
除了在错误的进程中重定向之外,使用
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 语言新手)。你能给出一个完整的例子吗?
好的。我已经放弃了我第一次编写的内置命令的函数指针的通用性;我的结论是这可能会让你更加困惑。我拒绝没有“打印错误”功能(尽管我经常加强它以报告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;
}
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)