Unix Shell 在 C 中实现 Cat - 文件描述符问题
我已经完成了 Unix shell 的实践实现,但当其输出到文件时,我在实现 cat
时遇到了问题; IE: cat foo.txt > bar.txt
- 将foo
的内容输出到bar
。
让我们从main函数开始然后我将定义子方法:
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
handle_user_input();
//determine input and execute foreground/background process
execute_command();
}
background = 0;
}
printf("\n[MYSHELL] $ ");
return 0;
}
handle_user_input
只是填充 cmd_argv
数组来执行 user_input
,并删除>
并设置 output
标志(如果用户希望输出到文件)。这是该方法的核心内容:
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
if(strcmp(cmd_argv[cmd_argc], ">") == 0){
printf("\nThere was a '>' in %s @ index: %d for buffer_pointer: %s \n", *cmd_argv,cmd_argc,buffer_pointer);
cmd_argv[cmd_argc] = strtok(NULL, " ");
output = 1;
}
cmd_argc++;
if(output){
filename = buffer_pointer;
printf("The return of handling input for filename %s = %s + %s \n", buffer_pointer, cmd_argv[0], cmd_argv[1]);
return;
}
}
然后调用 execute_command
,解释现在填充的 cmd_argv
。只是为了让您了解全局。显然,这些情况都不匹配,并且调用了 create_process
方法:
int execute_command()
{
if (strcmp("pwd", cmd_argv[0]) == 0){
printf("%s\n",getenv("PATH"));
return 1;
}
else if(strcmp("cd", cmd_argv[0]) == 0){
change_directory();
return 1;
}
else if (strcmp("jobs", cmd_argv[0]) == 0){
display_job_list();
return 1;
}
else if (strcmp("kill", cmd_argv[0]) == 0){
kill_job();
}
else if (strcmp("EOT", cmd_argv[0]) == 0){
exit(1);
}
else if (strcmp("exit", cmd_argv[0]) == 0){
exit(-1);
}
else{
create_process();
return;
}
}
非常简单,对吧?
create_process
是我遇到问题的地方。
void create_process()
{
status = 0;
int pid = fork();
background = 0;
if (pid == 0) {
// child process
if(output){
printf("Output set in create process to %d\n",output);
output = 0;
int output_fd = open(filename, O_RDONLY);
printf("Output desc = %d\n",output_fd);
if (output_fd > -1) {
dup2(output_fd, STDOUT_FILENO);
close(output_fd);
} else {
perror("open");
}
}
printf("Executing command, but STDOUT writing to COMMAND PROMPT instead of FILE - as I get the 'open' error above \n");
execvp(*cmd_argv,cmd_argv);
// If an error occurs, print error and exit
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(0);
} else {
// parent process, waiting on child process
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
}
return;
}
我打印了 output_fd = -1
,并且我设法在 else 中得到 perror("open")
,说明: open: No such file or directory.然后它打印出它是“写入命令提示符而不是文件”
,正如我在控制台上显示的那样。然后执行execvp
,它处理cat foo.txt
,但将其打印到控制台而不是文件。
我意识到此时不应该,因为 output_fd = -1
是不可取的,应该返回另一个值;但我无法弄清楚如何正确使用文件描述符以便使用 cat foo.txt > 打开新的/现有的文件bar.txt 并写入它,以及返回到命令行的标准输入。
我已设法输出到文件,但随后失去了正确的标准输入。有人可以指导我到这里吗?我觉得我正在为自己做错或正在寻找的一些愚蠢的事情而兜圈子。
非常感谢任何帮助。
I've about got my practice implementation of a Unix shell done, except I'm having an issue with implementing cat
when its output is to a file; IE: cat foo.txt > bar.txt
- outputting foo
's contents to bar
.
Let's start from the main function & then I'll define the submethods:
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
handle_user_input();
//determine input and execute foreground/background process
execute_command();
}
background = 0;
}
printf("\n[MYSHELL] $ ");
return 0;
}
handle_user_input
just populates the cmd_argv
array to execute the user_input
, and removes the >
and sets an output
flag if the user wishes to output to a file. This is the meat of that method:
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
if(strcmp(cmd_argv[cmd_argc], ">") == 0){
printf("\nThere was a '>' in %s @ index: %d for buffer_pointer: %s \n", *cmd_argv,cmd_argc,buffer_pointer);
cmd_argv[cmd_argc] = strtok(NULL, " ");
output = 1;
}
cmd_argc++;
if(output){
filename = buffer_pointer;
printf("The return of handling input for filename %s = %s + %s \n", buffer_pointer, cmd_argv[0], cmd_argv[1]);
return;
}
}
execute_command
is then called, interpreting the now populated cmd_argv
. Just to give you an idea of the big picture. Obviously, none of these cases match and the create_process
method is called:
int execute_command()
{
if (strcmp("pwd", cmd_argv[0]) == 0){
printf("%s\n",getenv("PATH"));
return 1;
}
else if(strcmp("cd", cmd_argv[0]) == 0){
change_directory();
return 1;
}
else if (strcmp("jobs", cmd_argv[0]) == 0){
display_job_list();
return 1;
}
else if (strcmp("kill", cmd_argv[0]) == 0){
kill_job();
}
else if (strcmp("EOT", cmd_argv[0]) == 0){
exit(1);
}
else if (strcmp("exit", cmd_argv[0]) == 0){
exit(-1);
}
else{
create_process();
return;
}
}
Pretty straight forward, right?
create_process
is where I'm having issues.
void create_process()
{
status = 0;
int pid = fork();
background = 0;
if (pid == 0) {
// child process
if(output){
printf("Output set in create process to %d\n",output);
output = 0;
int output_fd = open(filename, O_RDONLY);
printf("Output desc = %d\n",output_fd);
if (output_fd > -1) {
dup2(output_fd, STDOUT_FILENO);
close(output_fd);
} else {
perror("open");
}
}
printf("Executing command, but STDOUT writing to COMMAND PROMPT instead of FILE - as I get the 'open' error above \n");
execvp(*cmd_argv,cmd_argv);
// If an error occurs, print error and exit
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(0);
} else {
// parent process, waiting on child process
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
}
return;
}
My printed output_fd = -1
, and I manage to get the perror("open")
inside the else stating: open: No such file or directory
. It then prints that it's "writing to COMMAND PROMPT instead of FILE"
, as I display to the console. Then executes execvp
which handles cat foo.txt
, but prints it to the console instead of the file.
I realize it shouldn't at this point, as having output_fd = -1
isnt desirable and should be returning another value; but I cant figure out how to use file descriptors correctly in order to open a new/existing file with cat foo.txt > bar.txt
and write to it, as WELL AS GET BACK to the command line's stdin.
I have managed to output to the file, but then lose getting back the correct stdin. Could someone please direct me here? I feel like I'm going in circles over something silly I'm doing wrong or looking over.
Any help is greatly GREATLY appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果要写入文件,为什么要使用 O_RDONLY?我的猜测是你应该使用类似的东西:
(0666是在创建时设置访问权限)。
显然,如果您无法打开受限制的文件,则不应启动该命令。
Why do you use O_RDONLY if you want to write to the file? My guess is that you should use something like:
(The 0666 is to set up the access rights when creating).
And obviously, if you can't open a redicted file, you shouldn't launch the command.
首先,我注意到的明显的事情是您已经打开了文件 O_RDONLY。输出效果不太好!
其次,重定向输出的基本过程是:
,最后,您真的将命令名称作为全局变量传递吗?我认为一旦你尝试实现 cat foo | ,这个问题就会再次困扰你。 (猫栏;回声嗨;猫)> baz 或类似的东西。
First, obvious thing I notice is that you've opened the file O_RDONLY. Not going to work so well for output!
Second, basic process for redirecting the output is:
and finally, are you really passing around command names as global variables? I think this will come back to haunt you once you try and implement
cat foo | ( cat bar; echo hi; cat ) > baz
or somesuch.