6

この質問は、次の手順を実装しようとした結果です。

入力および出力としての Linux パイプ

パイプを使用して 2 つのプログラム間で単純な文字列を送信する方法は?

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
}

上記の文字列が正しく初期化されていることは間違いありません。ただし、私には意味をなさない2つのことが起こります。

(1) 私が実行しているプログラムは、「入力ファイルが空です」と報告します。「<」でプログラムを呼び出していないため、入力ファイルを期待するべきではありません。代わりに、キーボード入力を期待する必要があります。さらに、「gulp_command」に含まれるテキストを読み取っているはずです。

(2) プログラムのレポート (標準出力を介して提供される) がターミナルに表示されます。このパイプの目的は stdout を文字列 "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");

このプロセスをパイプを使用して置き換えたい。入力を標準入力としてプログラムに渡し、コードでそのプログラムからの標準出力を文字列に読み取らせたいと考えています。

4

3 に答える 3

7

Your primary problem is that you have the arguments to dup2() reversed. You need to use:

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

I got suckered into misreading what you wrote as OK until I put error checking on the set-up code and got unexpected values from the dup2() calls, which told me what the trouble was. When something goes wrong, insert the error checks you skimped on before.

You also did not ensure null termination of the data read from the child; this code does.

Working code (with diagnostics), using cat as the simplest possible 'other command':

#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;
}

Sample output:

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?)>>

Note that you will need to be careful to ensure you don't get deadlocked with your code. If you have a strictly synchronous protocol (so the parent writes a message and reads a response in lock-step), you should be fine, but if the parent is trying to write a message that's too big to fit in the pipe to the child while the child is trying to write a message that's too big to fit in the pipe back to the parent, then each will be blocked writing while waiting for the other to read.

于 2013-07-07T15:24:20.237 に答える
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;
}
于 2014-10-15T10:08:46.633 に答える