提问者:小点点

如何用C++实现linux shell中的管道命令?


我在一个实现迷你linux shell的项目中工作,我想实现一个管道命令,它的基本工作方式如下:command1 command2:使用管道字符“”将产生一个管道,将command1 stdout重定向到它的写通道,将command2 stdin重定向到它的读通道。

或者:

命令1(&Ccommand2:使用管道字符“&”将生成管道,将command1 stderr重定向到管道的写入通道,将command2 stdin重定向到管道的读取通道。

现在,命令1可以是我使用execv运行的linux外部命令,也可以是我编写的内置命令,而命令2始终是外部命令

我的代码工作不正确,我不知道问题到底出在哪里,因为我实现了许多命令,它们都工作得很完美,例如(cp,重定向。。。),所以在我的代码中,基础是好的,但管道就是错的!例如,如果命令是:showpid./parser.exe1,其中parser.exe是对命令进行解析的给定文件,例如这里如果showpid打印:shell process pid is 12311,那么调用此命令showpid./parser.exe1,输出应该是“shell”,但在我的代码中输出是shell process pid is 12311

这是我的管道命令实现:

这是管道命令的类:

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 .

        dup2(pipeFd[1], pipeNum);
        close(pipeFd[0]);
        close(pipeFd[1]);


        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[1]);
            close(pipeFd[0]);

            // 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 
            close(pipeFd[1]);
            close(pipeFd[0]);
            //if (waitpid(pid, NULL, 0) != -1) {
            //    perror("smash error: waitpid failed");
           // }
            waitpid(pid,NULL,0);
        }

    }
}

共1个答案

匿名用户

我认为您应该查看关闭/愚弄文件描述符的顺序。具体来说:

第一个命令需要使用现有的stdin(fd 0)。别关上。但是您应该关闭现有的stdout(fd 1),然后执行fd dup,使其变为1。

第二个命令则以相反的方式执行。

我将用一个简单得多的例子进行测试。让管道的工作,然后做执行的事情。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(何用|c++|linux|shell|中|管道|命令)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?