[英]how to implement pipe command from linux shell in c++?
我正在一个项目中实施一个迷你 linux shell,我想实施一个command1 | command2
命令,它基本上是这样工作的: command1 | command2
: 使用 pipe 字符“ |
” 将生成 pipe,将 command1 stdout 重定向到其写入通道,将 command2 stdin 重定向到其读取通道。
或者:
command1 |& command2
:使用 pipe 字符“ |&
”将生成 pipe,将 command1 stderr 重定向到管道的写入通道,将 command2 stdin 重定向到管道的读取通道。
现在命令 1 可以是我使用 execv 运行的 linux 的外部命令,也可以是我编写的内置命令,并且 command2 始终是外部命令
我的代码不能正常工作,我不知道问题到底出在哪里,因为我实现了许多命令,并且它们都工作得很好,例如(cp,重定向......),所以我的代码中的基础很好,但是pipe 是错误的:例如,如果命令是: showpid |./parser.exe 1
其中 parser.exe 是对命令进行解析的给定文件,例如这里如果 showpid 打印: shell process pid is 12311
,此命令showpid |./parser.exe 1
output 应该是"shell"
,但在我的代码中 output 是shell process pid is 12311
这是我的 pipe 命令实现:
这是 pipe 命令的 class :
class PipeCommand : public Command {
private:
int pipeNum;
int split;
string cmd1;
string cmd2;
public:
PipeCommand(const char* cmd_line);
virtual ~PipeCommand() {}
void execute() override;
};
// the pipe constructor , here i want to extract each command from the right and left side of the pipe from the cmd_line , which is the command line that i get
// fro example : " showpid | grep 1 "
PipeCommand::PipeCommand(const char* cmd_line):Command(cmd_line) {
pipeNum = -1;
isBackground = _isBackgroundComamnd(cmd_line);
string cmd1 = "", cmd2 = "";
int split = -1;
for (int i = 0; i < this->num_args; i++) {
if (strcmp(args[i], "|") == 0) {
split = i;
pipeNum = 1;
break;
}
if (strcmp(args[i], "|&") == 0) {
split = i;
pipeNum = 2;
break;
}
}
for (int i = 0; i < split; i++) {
cmd1 = cmd1 + args[i] + " ";
}
for (int i = split + 1; i < num_args; i++) {
cmd2 = cmd2 + args[i] + " ";
}
// the implementation of the pipe command
void PipeCommand::execute() {
int pipeFd[2];
int pid;
pipe(pipeFd);
pid = fork();
if (pid == 0) { // child process .
close(pipeFd[1]);
dup2(pipeFd[1], pipeNum);
if (isBuiltInCMD(args[0])) { // if the command is built in which means i wrote it i run it like this ( this works fine i checked it)
Command *newCmd = CreateBuiltInCommand(const_cast<char *>(cmd1.c_str()));
newCmd->execute();
exit(0);
} else { // if the command is external than use execv
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd1.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
}
} else { // the parent process , basically runs the command2 , which it can be only an external command
pid = fork(); // we fork again in the parent process
if (pid == 0) { // the child process executes the secomd command using execv
dup2(pipeFd[0], STDIN_FILENO);
close(pipeFd[0]);
dup2(pipeFd[0], pipeNum);
// execute
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd2.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
} else { // the parent process waits
waitpid(pid,NULL,0);
close(pipeFd[1]);
close(pipeFd[0]);
}
}
}
我认为您应该查看关闭/复制文件描述符的顺序。 具体来说:
第一个命令需要使用现有的标准输入 (fd 0)。 不要关闭它。 但是您应该关闭现有的标准输出(fd 1),然后执行 fd dup 使其变为 1。
第二个命令以另一种方式执行。
我会用一个更简单的例子来测试。 让管道工作,然后做 exec 的事情。
在 C/C++ 世界中,程序启动时有 3 个标准文件:
FD 0 是标准输入 -- 用于输入 FD 1 是标准输出 -- 用于正常 output FD 2 是标准错误 -- 用于错误 output
当你这样做时:
grep foo < file.txt | grep条
shell 所做的是:
- 是否调用 pipe 来获取输入和 output 文件 - 在第一个 grep 上用于 foo,关闭 fd 0 (stdin) 并打开文件。 它将落在 0 上,因此是 grep 命令的标准输入。 -关闭stdout并将其分配给pipe的out部分
在第二个 grep 上:
-关闭 1 (stdin) -并将 pipe 输入部分移动到 1,以便设置标准输入。
因此,最后:
第 1 部分 fd 0 (stdin) 是文件 第 1 部分 fd 1 (stdout) 是 pipe 的 output 部分 第 2 部分 fd 0 (stdin) 是 Z20826A3CB514DBCFE4E5C9Z 的输入部分
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.