阅读了一些答案后,我修改的代码是:
int pid = fork(); if (pid == -1) { perror("fork"); } else if (pid == 0) { if (in) { //if '<' char was found in string inputted by user int fd0 = open(input, O_RDONLY, 0); dup2(fd0, STDIN_FILENO); close(fd0); in = 0; } if (out) { //if '>' was found in string inputted by user int fd1 = creat(output, 0644); dup2(fd1, STDOUT_FILENO); close(fd1); out = 0; } execvp(res[0], res); perror("execvp"); _exit(1); } else { waitpid(pid, 0, 0); free(res); }
它的工作原理,但似乎标准输出不重新连接或这样的效果。 这是执行:
SHELL$ cat > file hello, world this is a test SHELL$ cat < file //no output SHELL$ ls //no output
'<'和'>'都可以工作,但是执行完后就没有输出了。
我一直在C中使用一个相对简单的shell,但是我在实现input(<)和输出(>)redirect时遇到了问题。 帮我find下面的代码中的问题:
int fd; int pid = fork(); int current_out; if (in) { //if '<' char was found in string inputted by user fd = open(input, O_RDONLY, 0); dup2(fd, STDIN_FILENO); in = 0; current_out = dup(0); } if (out) { //if '>' was found in string inputted by user fd = creat(output, 0644); dup2(fd, STDOUT_FILENO); out = 0; current_out = dup(1); } if (pid == -1) { perror("fork"); } else if (pid == 0) { execvp(res[0], res); perror("execvp"); _exit(1); } else { waitpid(pid, 0, 0); dup2(current_out, 1); free(res); }
我可能在这里有一些不必要的材料,因为我一直在尝试不同的东西来实现它。 我不确定发生了什么问题。
重定向之后,您有太多的文件描述符打开。 我们来剖析这两个段落:
if (in) { //if '<' char was found in string inputted by user fd = open(input, O_RDONLY, 0); dup2(fd, STDIN_FILENO); in = 0; current_in = dup(0); // Fix for symmetry with second paragraph } if (out) { //if '>' was found in string inputted by user fd = creat(output, 0644); dup2(fd, STDOUT_FILENO); out = 0; current_out = dup(1); }
我会变得慈善,忽略了你忽略了错误的事实。 但是,您将需要错误检查您的系统调用。
在第一段中,你打开一个文件并在变量fd
捕获文件描述符(很可能是3)。 然后您通过标准输入( STDIN_FILENO
)复制文件描述符。 但是请注意,文件描述符3仍然是打开的。 然后你做一个dup(0)
(为了一致性,应该是STDIN_FILENO
),得到另一个文件描述符,可能是4.所以你有文件描述符0,3和4指向相同的文件(并且,实际上,打开文件描述 – 注意到打开的文件描述不同于打开的文件描述符)。 如果你用current_in
的意图是保留(父)shell的标准输入,你必须在做dup()
之前做dup()
dup2()
来覆盖输出。 但是,最好不要改变父shell的文件描述符; 与重复复制文件描述符相比,其开销更小。
然后你或多或少地重复第二段中的过程,首先用fd = creat(...)
调用覆盖文件描述符3的唯一记录,然后获得一个新的描述符,大概是5,然后在标准输出上复制。 然后你做一个dup(1)
,产生另一个文件描述符,大概是6。
所以,你有主要的shell的标准输入和标准输出重定向到文件(并没有办法将它们恢复到原始值)。 因此,你的第一个问题是你在fork()
之前做重定向。 你应该在fork()
之后来做 – 虽然在进程之间进行管道连接时,你需要在分叉之前创建管道。
你的第二个问题是你需要关闭大量的文件描述符,其中一个你不再有参考。
所以,你可能需要:
if ((pid = fork()) < 0) ...error... else if (pid == 0) { /* Be childish */ if (in) { int fd0 = open(input, O_RDONLY); dup2(fd0, STDIN_FILENO); close(fd0); } if (out) { int fd1 = creat(output , 0644) ; dup2(fd1, STDOUT_FILENO); close(fd1); } ...now the child has stdin coming from the input file, ...stdout going to the output file, and no extra files open. ...it is safe to execute the command to be executed. execve(cmd[0], cmd, env); // Or your preferred alternative fprintf(stderr, "Failed to exec %s\n", cmd[0]); exit(1); } else { /* Be parental */ ...wait for child to die, etc... }
在执行这些操作之前,应确保已经刷新了shell的标准I / O通道,可能是使用fflush(0)
,这样如果分叉的子进程由于问题而写入标准错误,多余的重复输出。
还要注意,各种open()
调用应该被错误检查。
这是发生了什么事。 在调用fork()
,有两个执行的进程是原始进程的重复。 区别在于存储在pid
的fork()
的返回值。
然后,这两个进程(shell和子进程)将stdin和stdout重定向到相同的文件。 我想你是想在current_out
保存前一个fd,但是正如Seth Robertson所指出的那样,目前这个功能还不行,因为保存了错误的文件描述符。 父母还恢复其标准输出,但不是标准输入。
你可以修复这个错误,但是你可以做得更好。 你实际上不必重定向父母的输出,只是孩子的输出。 所以简单地检查一下pid
。 那么也不需要恢复任何文件描述符。
重定向之后,您有太多的文件描述符打开。 你需要的代码是这个。
if (pid == 0) { /* for the child process: */ // function for redirection ( '<' , '>' ) int fd0,fd1,i,in=0,out=0; char input[64],output[64]; // finds where '<' or '>' occurs and make that argv[i] = NULL , to ensure that command wont't read that for(i=0;argv[i]!='\0';i++) { if(strcmp(argv[i],"<")==0) { argv[i]=NULL; strcpy(input,argv[i+1]); in=2; } if(strcmp(argv[i],">")==0) { argv[i]=NULL; strcpy(output,argv[i+1]); out=2; } } //if '<' char was found in string inputted by user if(in) { // fdo is file-descriptor int fd0; if ((fd0 = open(input, O_RDONLY, 0)) < 0) { perror("Couldn't open input file"); exit(0); } // dup2() copies content of fdo in input of preceeding file dup2(fd0, 0); // STDIN_FILENO here can be replaced by 0 close(fd0); // necessary } //if '>' char was found in string inputted by user if (out) { int fd1 ; if ((fd1 = creat(output , 0644)) < 0) { perror("Couldn't open the output file"); exit(0); } dup2(fd1, STDOUT_FILENO); // 1 here can be replaced by STDOUT_FILENO close(fd1); } execvp(*argv, argv); perror("execvp"); _exit(1); // another syntax /* if (!(execvp(*argv, argv) >= 0)) { // execute the command printf("*** ERROR: exec failed\n"); exit(1); */ } else if((pid) < 0) { printf("fork() failed!\n"); exit(1); } else { /* for the parent: */ while (!(wait(&status) == pid)) ; // good coding to avoid race_conditions(errors) } }