【问题标题】:Piping for input/output输入/输出配管
【发布时间】:2013-07-04 17:20:00
【问题描述】:

这个问题源于我尝试执行以下指令:

Linux Pipes as Input and Output

How to send a simple string between two programs using pipes?

http://tldp.org/LDP/lpg/node11.html

我的问题与Linux Pipes as Input and Output 中的问题类似,但更具体。

基本上,我正在尝试替换:

/directory/program < input.txt > output.txt

在 C++ 中使用管道以避免使用硬盘。这是我的代码:

//LET THE PLUMBING BEGIN 
int fd_p2c[2], fd_pFc[2], bytes_read;
    // "p2c" = pipe_to_child, "pFc" = pipe_from_child (see above link)
pid_t childpid;
char readbuffer[80];
string program_name;// <---- includes program name + full path
string gulp_command;// <---- includes my line-by-line stdin for program execution
string receive_output = "";

pipe(fd_p2c);//create pipe-to-child
pipe(fd_pFc);//create pipe-from-child
childpid = fork();//create fork

if (childpid < 0)
{
    cout << "Fork failed" << endl;
    exit(-1);
}
else if (childpid == 0)
{
    dup2(0,fd_p2c[0]);//close stdout & make read end of p2c into stdout
    close(fd_p2c[0]);//close read end of p2c
    close(fd_p2c[1]);//close write end of p2c
    dup2(1,fd_pFc[1]);//close stdin & make read end of pFc into stdin
    close(fd_pFc[1]);//close write end of pFc
    close(fd_pFc[0]);//close read end of pFc

    //Execute the required program
    execl(program_name.c_str(),program_name.c_str(),(char *) 0);
    exit(0);
}
else
{
    close(fd_p2c[0]);//close read end of p2c
    close(fd_pFc[1]);//close write end of pFc

    //"Loop" - send all data to child on write end of p2c
    write(fd_p2c[1], gulp_command.c_str(), (strlen(gulp_command.c_str())));
    close(fd_p2c[1]);//close write end of p2c

    //Loop - receive all data to child on read end of pFc
    while (1)
    {        
        bytes_read = read(fd_pFc[0], readbuffer, sizeof(readbuffer));

        if (bytes_read <= 0)//if nothing read from buffer...
            break;//...break loop

        receive_output += readbuffer;//append data to string
    }
    close(fd_pFc[0]);//close read end of pFc
}

我绝对确定上述字符串已正确初始化。但是,发生了两件事对我来说没有意义:

(1) 我正在执行的程序报告“输入文件为空”。由于我没有使用“

(2) 程序的报告(通过标准输出提供)出现在终端中。这很奇怪,因为此管道的目的是将标准输出传输到我的字符串“receive_output”。但由于它出现在屏幕上,这向我表明信息没有通过管道正确传递到变量。如果我在 if 语句的末尾实现以下内容,

cout << receive_output << endl;

我什么也没得到,好像字符串是空的。感谢您给我的任何帮助!

编辑:澄清

我的程序当前使用文本文件与另一个程序通信。我的程序编写了一个文本文件(例如 input.txt),由外部程序读取。然后该程序生成 output.txt,由我的程序读取。所以它是这样的:

my code -> input.txt -> program -> output.txt -> my code

因此,我的代码目前使用,

system("program < input.txt > output.txt");

我想用管道替换这个过程。我想将我的输入作为标准输入传递给程序,并让我的代码将该程序的标准输出读入一个字符串。

【问题讨论】:

  • 你的起始命题不清楚。您声明要使用管道替换 /directory/program &lt;input.txt &gt;output.txt 以避免文件系统访问。但是您“需要”多个进程才能使使用管道变得明智。 (您可以在单个进程中使用管道,但通常没有意义。)因此,您可能拥有/directory/program1 &lt;input.txt | /directory/program2 &gt;output.txt;这是有道理的(您以前可能使用过/directory/program1 &lt;input.txt &gt;intermediate.txt; /directory/program2 &lt;intermediate.txt &gt;output.txt)。请说明您的意图。
  • 好点。我编辑了问题。
  • 作为一个额外的澄清,我的目标与您之前回答的问题基本相同:stackoverflow.com/questions/1734932/...(顺便说一句,答案很好)。
  • @EricInclan 如果我想为两个子进程做同样的事情,我们将如何运行此代码?意味着在忽略父进程的两个子进程之间发送和接收字符串。
  • @Mohsin 我以前从未尝试过。很遗憾,我目前无法为您提供指导。

标签: c++ linux pipe


【解决方案1】:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
using namespace std;
int main() {
    int i, status, len;
    char str[10];
    mknod("pipe", S_IFIFO | S_IRUSR | S_IWUSR, 0); //create named pipe
    pid_t pid = fork(); // create new process
    /* Process A */
    if (pid == 0) {
        int myPipe = open("pipe", O_WRONLY); // returns a file descriptor for the pipe
        cout << "\nThis is process A having PID= " << getpid(); //Get pid of process A
        cout << "\nEnter the string: ";
        cin >> str;
        len = strlen(str);
        write(myPipe, str, len); //Process A write to the named pipe
        cout << "Process A sent " << str;
        close(myPipe); //closes the file descriptor fields.
        }
    /* Process B */
        else {
        int myPipe = open("pipe", O_RDONLY); //Open the pipe and returns file descriptor
        char buffer[21];
        int pid_child;
        pid_child = wait(&status); //wait until any one child process terminates
        int length = read(myPipe, buffer, 20); //reads up to size bytes from pipe with descriptor fields, store results
    //  in buffer;
        cout<< "\n\nThis is process B having PID= " << getpid();//Get pid of process B
        buffer[length] = '\0';
        cout << "\nProcess B received " << buffer;
        i = 0;
        //Reverse the string
        for (length = length - 1; length >= 0; length--)
        str[i++] = buffer[length];
        str[i] = '\0';
        cout << "\nRevers of string is " << str;
        close(myPipe);
        }
    unlink("pipe");
return 0;
}

【讨论】:

    【解决方案2】:

    您的主要问题是dup2() 的参数颠倒了。你需要使用:

    dup2(fd_p2c[0], 0);   // Duplicate read end of pipe to standard input
    dup2(fd_pFc[1], 1);   // Duplicate write end of pipe to standard output
    

    在我对设置代码进行错误检查并从 dup2() 调用中得到意外值之前,我被误读为 OK,这告诉我问题出在哪里。当出现问题时,插入您之前跳过的错误检查。

    您也没有确保从孩子读取的数据为空终止;这段代码可以。

    工作代码(带诊断),使用cat 作为最简单的“其他命令”:

    #include <unistd.h>
    #include <string>
    #include <iostream>
    using namespace std;
    
    int main()
    {
        int fd_p2c[2], fd_c2p[2], bytes_read;
        pid_t childpid;
        char readbuffer[80];
        string program_name = "/bin/cat";
        string gulp_command = "this is the command data sent to the child cat (kitten?)";
        string receive_output = "";
    
        if (pipe(fd_p2c) != 0 || pipe(fd_c2p) != 0)
        {
            cerr << "Failed to pipe\n";
            exit(1);
        }
        childpid = fork();
    
        if (childpid < 0)
        {
            cout << "Fork failed" << endl;
            exit(-1);
        }
        else if (childpid == 0)
        {
            if (dup2(fd_p2c[0], 0) != 0 ||
                close(fd_p2c[0]) != 0 ||
                close(fd_p2c[1]) != 0)
            {
                cerr << "Child: failed to set up standard input\n";
                exit(1);
            }
            if (dup2(fd_c2p[1], 1) != 1 ||
                close(fd_c2p[1]) != 0 ||
                close(fd_c2p[0]) != 0)
            {
                cerr << "Child: failed to set up standard output\n";
                exit(1);
            }
    
            execl(program_name.c_str(), program_name.c_str(), (char *) 0);
            cerr << "Failed to execute " << program_name << endl;
            exit(1);
        }
        else
        {
            close(fd_p2c[0]);
            close(fd_c2p[1]);
    
            cout << "Writing to child: <<" << gulp_command << ">>" << endl;
            int nbytes = gulp_command.length();
            if (write(fd_p2c[1], gulp_command.c_str(), nbytes) != nbytes)
            {
                cerr << "Parent: short write to child\n";
                exit(1);
            }
            close(fd_p2c[1]);
    
            while (1)
            {
                bytes_read = read(fd_c2p[0], readbuffer, sizeof(readbuffer)-1);
    
                if (bytes_read <= 0)
                    break;
    
                readbuffer[bytes_read] = '\0';
                receive_output += readbuffer;
            }
            close(fd_c2p[0]);
            cout << "From child: <<" << receive_output << ">>" << endl;
        }
        return 0;
    }
    

    样本输出:

    Writing to child: <<this is the command data sent to the child cat (kitten?)>>
    From child: <<this is the command data sent to the child cat (kitten?)>>
    

    请注意,您需要小心确保您的代码不会陷入僵局。如果你有一个严格同步的协议(所以父母写一条消息并在锁步中读取一个响应),你应该没问题,但是如果父母试图写一条太大而无法放入管道的消息给孩子当孩子试图将一个太大而无法放入管道中的消息写回给父母时,每个人都会在等待另一个人阅读时被阻止写入。

    【讨论】:

    • 这很好用。感谢您的帮助,以及有关死锁代码的提示。我认为这个程序不会有这个问题,但很高兴知道。
    • 如果我想为两个子进程做同样的事情,我们将如何运行这段代码?意味着在忽略父进程的两个子进程之间发送和接收字符串。
    • @Mohsin:这部分取决于子进程将如何通信,以及启动两个子进程后父进程将做什么。有多种方法可以处理它。一种选择是在执行上述任何代码之前简单地拥有父分叉,并且该分叉的父进程可以退出或等待或继续其他工作,而子进程按上述方式处理代码。或者,父进程设置两个管道,分叉两次,每个子进程整理自己的管道,而父进程关闭两个管道的四个文件描述符并按照自己的方式进行。
    • 是的,我完全按照您的建议进行了第二种方法,我只是简单地创建一个管道并调用两个分叉,一个用于 child_a,另一个用于 child_b,如果 pid_child_a 为零,它会写入管道,否则如果 pid_child_b为零它从缓冲区读取并打印值,问题是当我第一次运行它时它运行良好,但第二次运行它什么也没打印。有时,当我清理项目或再次进行一些编辑时,会给出正确的输出,但随后又开始不打印任何内容,依此类推。
    • 如果我在调试模式下运行它,它每次都会给出正确的输出
    【解决方案3】:

    听起来您正在寻找coprocesses。您可以使用 C/C++ 对它们进行编程,但由于它们已经在 (bash) shell 中可用,因此更易于使用 shell,对吧?

    首先使用 coproc 内置函数启动外部程序:

    coproc external_program
    

    coproc 在后台启动程序并将文件描述符存储在数组 shell 变量中以与其通信。现在你只需要启动你的程序将它连接到那些文件描述符:

    your_program <&${COPROC[0]} >&${COPROC[1]}
    

    【讨论】:

    • 就我而言,我的程序反复调用 external_program(数千次),每次都使用不同的输入。这似乎是一个在启动时执行的一次性 bash 脚本,对吧?我对文件描述符了解不多,但是如果我要使用它,这是否意味着我必须写入驱动器上的文件,或者文件描述符可以指向我代码中的字符串?
    • 在这种情况下,文件描述符连接到管道,而不是文件。此外,您还可以运行 bash 脚本数千次。
    猜你喜欢
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2012-12-02
    • 1970-01-01
    相关资源
    最近更新 更多