2013-07-07 42 views
5

Câu hỏi này sau từ nỗ lực của tôi để thực hiện các hướng dẫn trong:Piping cho đầu vào/đầu ra

Linux Pipes as Input and Output

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

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

Câu hỏi của tôi là dọc theo dòng của câu hỏi trong: Linux Pipes as Input and Output, nhưng cụ thể hơn.

Về cơ bản, tôi đang cố gắng để thay thế:

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

sử dụng đường ống trong C++ để tránh sử dụng ổ cứng. Đây là mã của tôi:

//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 
} 

Tôi hoàn toàn chắc chắn rằng các chuỗi ở trên được khởi tạo đúng cách. Tuy nhiên, hai điều xảy ra không hợp lý với tôi:

(1) Chương trình tôi đang thực hiện báo cáo rằng "tệp đầu vào trống." Vì tôi không gọi chương trình với "<" nên không nên mong đợi tệp đầu vào. Thay vào đó, nó sẽ được mong đợi đầu vào bàn phím. Hơn nữa, nó nên được đọc văn bản chứa trong "gulp_command."

(2) Báo cáo của chương trình (được cung cấp qua đầu ra tiêu chuẩn) xuất hiện trong thiết bị đầu cuối. Điều này là kỳ lạ vì mục đích của đường ống này là chuyển stdout thành chuỗi của tôi "receive_output". Nhưng vì nó xuất hiện trên màn hình, điều đó cho tôi biết rằng thông tin không được truyền chính xác qua đường ống đến biến đó. Nếu tôi triển khai các điều sau ở cuối câu lệnh if,

cout << receive_output << endl; 

Tôi không nhận được gì, như chuỗi rỗng. Tôi đánh giá cao bất kỳ giúp bạn có thể cho tôi!

CHỈNH SỬA: Làm rõ

Chương trình của tôi hiện đang giao tiếp với một chương trình khác bằng tệp văn bản. Chương trình của tôi viết một tệp văn bản (ví dụ: input.txt), được chương trình bên ngoài đọc. Chương trình đó sau đó tạo output.txt, được đọc bởi chương trình của tôi. Vì vậy, nó là một cái gì đó như thế này:

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

Do đó, mã của tôi hiện đang sử dụng,

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

Tôi muốn thay thế quá trình này sử dụng ống. Tôi muốn chuyển đầu vào của tôi như là đầu vào tiêu chuẩn cho chương trình, và có mã của tôi đọc đầu ra tiêu chuẩn từ chương trình đó thành một chuỗi.

+0

Đề xuất bắt đầu của bạn không rõ ràng. Bạn nói rằng bạn muốn thay thế '/ directory/program output.txt' bằng một cái gì đó bằng cách sử dụng đường ống để tránh truy cập hệ thống tập tin. Nhưng bạn cần 'nhiều quy trình để sử dụng ống hợp lý. (Bạn _can_ sử dụng đường ống trong một quá trình duy nhất, nhưng nó không có ý nghĩa thường.) Vì vậy, bạn có thể có '/ directory/program1 output.txt'; có ý nghĩa (và trước đó bạn có thể đã sử dụng '/ directory/program1 intermediate.txt;/directory/program2 output.txt'). Vui lòng làm rõ ý định của bạn. –

+0

Điểm tốt. Tôi đã chỉnh sửa câu hỏi. –

+0

Làm rõ thêm, mục tiêu của tôi về cơ bản giống như câu hỏi trong: stackoverflow.com/questions/1734932/… mà bạn đã trả lời trước đây (câu trả lời tuyệt vời bằng cách này). –

Trả lời

7

Vấn đề chính của bạn là bạn có đối số là dup2() bị đảo ngược. Bạn cần sử dụng:

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 

tôi đã suckered vào hiểu sai những gì bạn đã viết như OK cho đến khi tôi đặt kiểm tra lỗi trên mã set-up và có giá trị bất ngờ từ dup2() cuộc gọi, mà nói với tôi những gì rắc rối là. Khi xảy ra sự cố, hãy chèn lỗi kiểm tra bạn đã lướt qua trước đây.

Bạn cũng không đảm bảo việc chấm dứt dữ liệu được đọc từ con; mã này.

đang làm việc (với chẩn đoán), sử dụng cat như đơn giản nhất có thể 'lệnh khác: sản lượng

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

mẫu:

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

Lưu ý rằng bạn sẽ cần phải cẩn thận để đảm bảo bạn không bị bế tắc với mã của mình. Nếu bạn có một giao thức đồng bộ nghiêm ngặt (để cha mẹ viết một tin nhắn và đọc một phản hồi trong bước khóa), bạn nên ổn, nhưng nếu cha mẹ đang cố gắng viết một tin nhắn quá lớn để vừa với ống dẫn đến đứa trẻ trong khi đứa trẻ đang cố gắng viết một thông điệp quá lớn để vừa vặn trong ống trở lại với bố mẹ, thì mỗi đứa sẽ bị chặn viết trong khi đợi người kia đọc.

+1

Điều này làm việc rất đẹp. Cảm ơn bạn đã giúp đỡ của bạn, và cho người đứng đầu về mã bế tắc. Tôi không nghĩ rằng tôi sẽ có vấn đề với chương trình này, nhưng nó là tốt để biết. –

+0

làm cách nào để chạy mã này nếu tôi muốn làm điều tương tự nhưng đối với hai quy trình con? nghĩa là việc gửi và nhận các chuỗi giữa hai tiến trình con bỏ qua phụ huynh. – Mohsin

+1

@Mohsin: nó phụ thuộc một phần vào cách thức trẻ em sẽ giao tiếp, và về những gì quá trình cha mẹ sẽ làm sau khi bắt đầu hai đứa trẻ. Có nhiều cách để đối phó với nó.Một tùy chọn là chỉ cần có nhánh cha trước khi thực hiện bất kỳ mã nào ở trên và quy trình gốc từ ngã ba đó có thể thoát hoặc chờ hoặc tiếp tục với công việc khác, trong khi quá trình con xử lý mã như trên. Ngoài ra, quá trình cha mẹ thiết lập hai ống, dĩa hai lần, và mỗi đứa trẻ sắp xếp đường ống dẫn nước của chính nó trong khi cha mẹ đóng bốn bộ mô tả tập tin cho hai ống và đi theo cách riêng của nó. –

1

Có vẻ như bạn đang tìm kiếm coprocesses. Bạn có thể lập trình chúng trong C/C++ nhưng vì chúng đã có sẵn trong shell (bash), dễ sử dụng hơn, phải không?

Đầu tiên khởi động chương trình bên ngoài với coproc dựng sẵn:

coproc external_program 

Các coproc bắt đầu chương trình ở chế độ nền và lưu trữ các file descriptor để giao tiếp với nó trong một biến mảng vỏ.Bây giờ bạn chỉ cần bắt đầu chương trình của bạn kết nối nó với các bộ mô tả tệp đó:

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

Trong trường hợp của tôi, chương trình của tôi liên tục gọi external_program (hàng nghìn lần), mỗi lần với đầu vào khác nhau. Điều này có vẻ giống như một kịch bản bash một lần được thực thi khi khởi chạy, đúng không? Tôi không biết nhiều về các bộ mô tả tập tin, nhưng nếu tôi sử dụng nó, điều này có nghĩa là tôi phải ghi vào một tập tin trên ổ đĩa, hoặc bộ mô tả tập tin có trỏ tới một chuỗi trong mã của tôi không? –

+1

Các bộ mô tả tệp được kết nối với đường ống trong trường hợp này, không phải tệp. Ngoài ra, bạn cũng có thể chạy một tập lệnh bash hàng ngàn lần. – Joni

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; 
} 
Các vấn đề liên quan