[英]Pipes, dup2 and exec()
我必须写一个可以运行管道的shell。 例如像ls -l | wc -l
这样的命令 ls -l | wc -l
"。我已经成功解析了用户给出的命令,如下所示:
"ls" = firstcmd
“-l” = frsarg
"wc" = scmd
“-l” = secarg
现在我必须使用两个叉子,因为命令是两个和一个管道。 我为执行命令而编写的代码块如下:
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
execlp(scmd, scmd, secarg, (char*) NULL);
}
}
所以当我运行我的 shell 并输入命令ls -l | wc -l
ls -l | wc -l
(例如) execs 的结果没有显示,但 shell 继续正常运行。
奇怪的是,只有当我用“exit”或“^C”终止我的shell时,命令的结果才会显示。
那个输出有什么问题? 为什么我输入命令后不显示?
您需要关闭父进程和子进程中的所有管道描述符(在子进程中重复之后)。 在您的代码中,主要问题是wc
进程没有退出,因为仍然有作者存在(因为父进程没有关闭写端)。 变化如下所示。 我还在父进程中添加了waitpid
来等待wc
进程。
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
close(fd[WRITE_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
exit(1);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
execlp(scmd, scmd, secarg,(char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", scmd);
exit(1);
}
else
{
int status;
close(fd[READ_END]);
close(fd[WRITE_END]);
waitpid(pid, &status, 0);
}
}
嗯,够近了。 您错过了在 fork 之后处理关闭某些文件描述符的操作。
这里有一些参考:
这是我的代码:
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h> //
//
#define INPUT_END 1 // INPUT_END means where the pipe takes input
#define OUTPUT_END 0 // OUTPUT_END means where the pipe produces output
//
int main(int argc, char* argv[]) //
{ //
pid_t pid1; // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
pid_t pid2; //
int fd[2]; //
//
pipe(fd); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input, fd[1] -> pipe_output]
pid1 = fork(); //
//
if(pid1==0) //
{ // I am going to be the wc process (i.e. taking input from the pipe)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
dup2(fd[OUTPUT_END], STDIN_FILENO); // [STDIN -> pipe_output, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
close(fd[OUTPUT_END]); // [STDIN -> pipe_output, STDOUT -> terminal_output] (of the WC process)
execlp("wc", "wc", "-l",(char*) NULL); //
} //
else //
{ //
pid2=fork(); //
//
if(pid2==0) //
{ // I am going to be the ls process (i.e. producing output to the pipe)
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the ls process)
dup2(fd[INPUT_END], STDOUT_FILENO); // [STDIN -> terminal_input, STDOUT -> pipe_input, fd[0] -> pipe_input] (of the ls process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> pipe_input] (of the ls process)
execlp("ls","ls","-l",(char*) NULL); //
} //
//
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the parent process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
waitpid(-1, NULL, 0); // As the parent process - we wait for a process to die (-1) means I don't care which one - it could be either ls or wc
waitpid(-1, NULL, 0); // As the parent process - we wait for the another process to die.
// At this point we can safely assume both process are completed
} //
} //
嗯,对于这样的事情,一个简单而有效的工作是使用管道内容制作脚本,然后使用 C 代码中的一些 exec 命令调用脚本。
脚本文件
#!/bin/sh
ls -l | wc -l
而你只是在你的 C 程序中做这样的事情
char *argv[] = {"script.sh", NULL};
execv(argv[0], argv);
请注意,您必须将 script.sh 放在 C 程序的同一目录中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.