【问题标题】:How to handle pipe channels correctly in order to implement pipe command (similar to pipe command in linux)?如何正确处理管道通道以实现管道命令(类似于 linux 中的管道命令)?
【发布时间】:2020-12-05 18:49:59
【问题描述】:

我想实现一个管道命令(类似于 linux bash 中的这个),它基本上是这样工作的: command1 | command2:使用管道字符“|”将产生一个管道,将 command1 stdout 重定向到其写入通道,将 command2 stdin 重定向到其读取通道。

或:

command1 |& command2:使用管道字符“|&”将产生一个管道,将 command1 stderr 重定向到管道的写入通道,将 command2 stdin 重定向到管道的读取通道。

现在命令 1 可以是我使用 execv 运行的来自 linux 的外部命令,也可以是我编写的内置命令,并且 command2 始终是外部命令

我的代码工作不正常,我实现了许多命令,并且它们都工作得很好,例如(cp,重定向......),所以我的代码中的基础很好,但是管道是错误的!例如,如果命令是:showpid | ./parser.exe 1 其中 parser.exe 是一个对命令进行解析的给定文件,例如这里如果 showpid 打印:shell process pid is 12311,然后调用此命令showpid | ./parser.exe 1,输出应该是"shell",但在我的代码中,输出是@987654329 @

代码不起作用的原因是因为我正在处理管道的通道和标准输入/标准输出错误!我尝试了所有可能的组合,但还是有问题!

这是我的管道命令实现:

这是管道命令的类:

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]);
        }

【问题讨论】:

  • 使用popen会更容易。
  • 我用过管道:(

标签: c++ linux pipe


【解决方案1】:

这段代码制作了管道,但我认为问题可能出在Parser.exe 程序没有得到第一个单词,而是所有的输入。

int main(int argc, char** argv)
{
    int pipes[2] {0};
    int pid {0};

    if (argc < 3) {
        printf ("No file to cat and/or no text to grep.\n");
        return 1;
    }

    pipe (pipes);
    pid = fork();
    if (pid == 0) {
        dup2 (pipes[1], STDOUT_FILENO);
        close (pipes[1]);
        close (pipes[0]);
        execl ("/bin/cat", "/bin/cat", argv[1], nullptr);
    } else {
        int pid = fork();
        if (pid == 0) {
            dup2 (pipes[0], STDIN_FILENO);
            close (pipes[1]);
            close (pipes[0]);
            execl ("/bin/grep", "/bin/grep", argv[2], nullptr);
        } else {
            close (pipes[0]);
            close (pipes[1]);
            waitpid (pid, nullptr, 0);
        }
    }
    
    return 0;
}

它是有效的,但你可能需要适应你的班级。

这是我的输出:

manuel@desktop:~/projects$ ./main sync_client.cpp boost
Going to cat sync_client.cpp
Going to grep for boost
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
      std::cout << "  sync_client www.boost.org /LICENSE_1_0.txt\n";
    boost::asio::io_service io_service;
    boost::system::error_code error = boost::asio::error::host_not_found;
      throw boost::system::system_error(error);
    boost::asio::streambuf request;
    boost::asio::write(socket, request);
    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");
    boost::asio::read_until(socket, response, "\r\n\r\n");
    while (boost::asio::read(socket, response,
          boost::asio::transfer_at_least(1), error))
    if (error != boost::asio::error::eof)
      throw boost::system::system_error(error);

【讨论】:

  • 这里你没有关闭任何频道,我们也应该像以前一样返回标准输入和标准输出频道吗?我也需要等待命令,而不是在后台(这我会在我弄清楚如何先在前台运行之后再做)
  • 我没有关闭,因为我很懒。我添加了它们。
  • 哦,我开始认为没有必要关闭它们了,哈哈
  • 最好关闭它们,否则命令可能会挂起等待输入。
  • 好的,我要试试这个,并在这些更改后立即尝试调试
猜你喜欢
  • 2012-06-18
  • 1970-01-01
  • 2019-02-09
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
相关资源
最近更新 更多