在 shell 中 运行 cat 命令时文件描述符错误
Bad file descriptor when running cat command in a shell
我正在尝试在我为类 Unix OS (xV6) 编写的 shell 中实现 I/O 重定向。在我正在阅读的 OS 手册中,我发现以下代码会在 shell 中 运行 执行 cat 命令:
char *argv[2];
argv[0] = "cat";
argv[1] = 0;
if(fork() == 0) {
close(0);
open("input.txt", O_RDONLY);
exec("cat", argv);
}
我将我的 shell 中的代码修改为 运行,它的 argv 数组位于另一个函数中,但它保持了功能。出于某种原因,当我 运行 cat < input.txt
时 shell 输出:
cat: -: Bad file descriptor
cat: closing standard input: Bad file descriptor
我还是 OS 编程的新手,所以我不太清楚 I/O 重定向的所有功能,但我认为我的代码应该可以工作。是什么导致了问题。我有下面 I/O 重定向的代码:
case '<':
ecmd = (struct execcmd*)cmd;
rcmd = (struct redircmd*)cmd;
if(fork() == 0){
close(0);
open("input", O_RDONLY);
execvp(ecmd->argv[0], ecmd->argv );
}
runcmd(rcmd->cmd);
break;
编辑
我做了 strace -e open ls
并得到:
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
a.out sh.c test
+++ exited with 0 +++
编辑 2
出于某种原因,这个案例的代码有效,但我不确定为什么:
case '<':
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode) < 0){
printf(2, "Cannot open file %s\n", rcmd->file);
perror(rcmd->file);
exit(1);
}
runcmd(rcmd->cmd);
break;
您不能只关闭 stdin 然后打开另一个文件。它不会自动成为您的新标准输入。您想要的是使用系统调用 dup2,它可以 "redirect" 一个文件描述符到另一个文件描述符。
int fd = open("input.txt", O_RDONLY);
dup2(fd, 0); // stdin now points to fd
close(fd);
有关详细信息,请参阅 man 2 dup2。请注意,open 和 dup2 都可能会失败,因此如果担心这一点,您应该检查它们的 return 值。
编辑:这有时会起作用,因为POSIX保证内核总是分配最低的空闲文件描述符。但它不是线程安全的,一般来说只是糟糕的风格。我建议始终执行 dup2,即使在某些情况下您可以不使用它。
我正在尝试在我为类 Unix OS (xV6) 编写的 shell 中实现 I/O 重定向。在我正在阅读的 OS 手册中,我发现以下代码会在 shell 中 运行 执行 cat 命令:
char *argv[2];
argv[0] = "cat";
argv[1] = 0;
if(fork() == 0) {
close(0);
open("input.txt", O_RDONLY);
exec("cat", argv);
}
我将我的 shell 中的代码修改为 运行,它的 argv 数组位于另一个函数中,但它保持了功能。出于某种原因,当我 运行 cat < input.txt
时 shell 输出:
cat: -: Bad file descriptor
cat: closing standard input: Bad file descriptor
我还是 OS 编程的新手,所以我不太清楚 I/O 重定向的所有功能,但我认为我的代码应该可以工作。是什么导致了问题。我有下面 I/O 重定向的代码:
case '<':
ecmd = (struct execcmd*)cmd;
rcmd = (struct redircmd*)cmd;
if(fork() == 0){
close(0);
open("input", O_RDONLY);
execvp(ecmd->argv[0], ecmd->argv );
}
runcmd(rcmd->cmd);
break;
编辑
我做了 strace -e open ls
并得到:
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
a.out sh.c test
+++ exited with 0 +++
编辑 2
出于某种原因,这个案例的代码有效,但我不确定为什么:
case '<':
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode) < 0){
printf(2, "Cannot open file %s\n", rcmd->file);
perror(rcmd->file);
exit(1);
}
runcmd(rcmd->cmd);
break;
您不能只关闭 stdin 然后打开另一个文件。它不会自动成为您的新标准输入。您想要的是使用系统调用 dup2,它可以 "redirect" 一个文件描述符到另一个文件描述符。
int fd = open("input.txt", O_RDONLY);
dup2(fd, 0); // stdin now points to fd
close(fd);
有关详细信息,请参阅 man 2 dup2。请注意,open 和 dup2 都可能会失败,因此如果担心这一点,您应该检查它们的 return 值。
编辑:这有时会起作用,因为POSIX保证内核总是分配最低的空闲文件描述符。但它不是线程安全的,一般来说只是糟糕的风格。我建议始终执行 dup2,即使在某些情况下您可以不使用它。