我需要自己实现shell,但无法正确实现管道。
如果我有类似ls -l | grep toto
的命令,我需要将ls -l
的输出(stdout)重定向到grep toto
的输入(stdin)。
我还希望在execvp调用时在父级而不是直接在子级中显示命令的结果(这就是为什么我有两个fork)。
目前,使用下面的代码,我创建了一个子对象来使用execvp执行命令,然后将结果写入一个变量。家长得到并展示它。
我还引入了另一个fork来正确执行我的管道。
它正在工作(它执行并显示正确的结果),但我的孩子从未完成,execvp在管道后执行我的第二个命令后被阻塞。
通过一些研究,我发现这可能是因为我没有关闭我的一个文件描述符,但我检查了多次,我真的没有看到问题或描述符没有关闭这里。。。
有人知道我做错了什么吗?
我不是很擅长c语言,叉子和管子对我来说有着很深的魔力,我也不太明白它到底是怎么工作的。。。所以如果你有什么建议或者觉得我做的不好,可以在评论中说出来。
以下是我的功能:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h> // open function
#include<sys/types.h>
#include<sys/stat.h>
#include<getopt.h>
#include<stdbool.h>
#define STDOUT 1
#define STDERR 2
int main(int argc, char** argv)
{
//PARENT
pid_t pid, wpid, pid2;
int status, status2;
const int MIN_BUFF_SIZE = 1024;
int fd[2];
if (pipe(fd) == -1) {
perror("pipe");
}
printf("After creating pipe \n");
pid = fork();
if (pid == 0) {
// First child process (parent of second child)
printf("First child process before fork again\n");
pid2 = fork();
if(pid2 == 0)
{
printf("Second child process begin\n");
//second child we need to execute the left command
close(fd[0]);
printf("Second child |Redirect stdout > stdin\n");
dup2(fd[1], STDOUT_FILENO); // Redirect stdout to stdin
//test data
char* test[3];
test[0] = "ls\0";
test[1] = "-l\0";
test[2] = NULL;
//TODO test to remove
if (execvp(test[0], test) == -1) {
perror("shell launch error : error in child process > execvp failed \n");
exit(errno);
}
printf("Second child | After execvp\n");
exit(errno);
}else if(pid<0)
{
perror("shell launch error : error forking second child");
}else{
do {
wpid = waitpid(pid2, &status2, WUNTRACED);
printf("Second parent\n");
//Parent
close(fd[1]);
printf("Second parent | Redirect stdout > stdin\n");
dup2(fd[0], STDIN_FILENO);
printf("Second parent | After Redirect stdout > stdin\n");
//test data : grep toto
char* test2[3];
test2[0] = "grep\0";
test2[1] = "toto\0";
test2[2] = NULL;
printf("Second parent | Av dup2 fd stdout\n");
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
printf("Second parent | Ap dup2 fd stdout\n");
if (execvp(test2[0], test2) == -1) {
perror("shell launch error : error in child process > execvp failed \n");
exit(errno);
}
exit(errno);
} while (!WIFEXITED(status2) && !WIFSIGNALED(status2));
}
} else if (pid < 0) {
// Error forking
perror("shell launch error : error forking");
} else {
do {
//wait child process to finish it execution. So, according to project requirements,
//we need to print the result of the command after the child process is finished
wpid = waitpid(pid, &status, WUNTRACED);
printf("Finished waiting for %d\n", wpid);
close(fd[1]); // close the write end of the pipe in the parent
if(status != 0)
{
printf("Status : %d\n", status);
}else{
printf("We are in the first parent \n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
printf("Finish ! \n");
return 0;
}
下面是执行后的输出:
After creating pipe
First child process before fork again
Second child process begin
Second child |Redirect stdout > stdin
Second parent
Second parent | Redirect stdout > stdin
Second parent | After Redirect stdout > stdin
Second parent | Av dup2 fd stdout
Second parent | Ap dup2 fd stdout
-rw-r--r-- 1 ubuntu ubuntu 0 Mar 8 08:20 toto.txt
(and here it does not come back to my shell, it is waiting... something...)
我看到很多关于Implementing pipe in c和Connecting n commands with pipes in a shell?的话题,但他们不像我一样有两个叉子,这是我的问题。
编辑
我用@William和@Toby suggestions编辑我的文章(用注释中给出的模式关闭描述符,在等待孩子之前关闭祖父母进程中的write end)。我添加了一个注释//newforallnewline来帮助有相同问题的其他人查看更改。我的程序不再被execvp阻止。
我总是有一个问题,如果在我的祖父母的过程中,我试图读标准输出,他们是什么都没有,而不是我的命令,我错过了一个重定向或我关闭了许多描述符这次?
//PARENT
pid_t pid, wpid, pid2;
int status, status2;
const int MIN_BUFF_SIZE = 1024;
int fd[2];
if (pipe(fd) == -1) {
perror("pipe");
}
printf("After creating pipe \n");
pid = fork();
if (pid == 0) {
// First child process (parent of second child)
printf("First child process before fork again\n");
pid2 = fork();
if(pid2 == 0)
{
printf("Second child process begin\n");
//second child we need to execute the left command
close(fd[0]);
printf("Second child |Redirect stdout > stdin\n");
dup2(fd[1], STDOUT_FILENO); // Redirect stdout to stdin
close(fd[1]); // NEW
//test data
char* test[3];
test[0] = "ls\0";
test[1] = "-l\0";
test[2] = NULL;
//TODO test to remove
if (execvp(test[0], test) == -1) {
perror("shell launch error : error in child process > execvp failed \n");
exit(errno);
}
printf("Second child | After execvp\n");
exit(errno);
}else if(pid<0)
{
perror("shell launch error : error forking second child");
}else{
do {
wpid = waitpid(pid2, &status2, WUNTRACED);
printf("Second parent\n");
//Parent
close(fd[1]);
printf("Second parent | Redirect stdout > stdin\n");
dup2(fd[0], STDIN_FILENO);
close(fd[0]); // NEW
printf("Second parent | After Redirect stdout > stdin\n");
//test data : grep toto
char* test2[3];
test2[0] = "grep\0";
test2[1] = "toto\0";
test2[2] = NULL;
printf("Second parent | Av dup2 fd stdout\n");
close(fd[0]); // NEW
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
printf("Second parent | Ap dup2 fd stdout\n");
if (execvp(test2[0], test2) == -1) {
perror("shell launch error : error in child process > execvp failed \n");
exit(errno);
}
exit(errno);
} while (!WIFEXITED(status2) && !WIFSIGNALED(status2));
}
} else if (pid < 0) {
// Error forking
perror("shell launch error : error forking");
} else {
do {
close(fd[1]); //NEW close the write end of the pipe in the parent
//wait child process to finish it execution. So, according to project requirements,
//we need to print the result of the command after the child process is finished
wpid = waitpid(pid, &status, WUNTRACED);
printf("Finished waiting for %d\n", wpid);
char* line_to_display = malloc(1);
line_to_display = '\0';
if(status != 0)
{
printf("Status : %d\n", status);
}else{
printf("We are in the first parent \n");
ssize_t bytes_read = 1;
do {
line_to_display = realloc(line_to_display, 1024);
//sizeof(char) = 1 so don't need to do MIN_BUFF_SIZE * sizeof(char)
bytes_read = read(fd[0], line_to_display, 1024);
} while (bytes_read > 0);
printf("%s\n", line_to_display);
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
printf("Finish ! \n");
return 0;
}
最佳答案
在祖父母进程中,管道的写入端仍处于打开状态。我们可以通过在第一个子对象中创建管道来避免此问题,如下所示:
pid = fork();
if (pid == 0) {
// First child process (parent of second child)
int fd[2];
if (pipe(fd) == -1) {
perror("pipe");
}
fprintf(stderr, "After creating pipe \n");
fprintf(stderr, "First child process before fork again\n");
pid2 = fork();
然后我们需要删除现在超出范围的
close(fd[1]);
(依赖于编译器)。我顺便注意到有一个输入错误:
if(pid<0)
而不是pid2<0
。关于c - 如何在C中使用fork和pipe和execvp将stdout重定向到stdin?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49407406/