簡體   English   中英

C ++啟動腳本(bash,python…)並使用stdin / stdout進行數據傳輸[linux]

[英]C++ start a script ( bash, python … ) and using stdin/stdout for datatransfare [linux]

我想從我的C ++代碼中調用python腳本。 python腳本看起來像:

    hello.py
    #!/usr/bin/python
    import sys
    print "Hello!"
    Readin = sys.stdin.read()
    print Readin

C ++代碼來自堆棧溢出的另一個問題。 它應該如何工作:

  • 創建一對管道。

  • fork()創建一個子進程。

  • 孩子正在將其管道彎曲到標准輸入/標准輸出。 關閉另一端並啟動腳本。

  • 父親正在聽管道read() ,接收輸入。 然后,發送一條消息write()

當輸入switch (readResult = read(childToPa... )時,程序不會從行返回。

我也不知道本文是否正在發揮作用。 這樣做是一個有前途的想法,還是還有其他可行的方法? 謝謝!

看起來像:

// maybe not all includes are necessary
#include <iostream>
#include <fstream>
#include <string>
#include <errno.h>
#include <sys/stat.h> // mkdir
#include <stdlib.h>     // system()
#include <unistd.h> // rmdir
#include <cstring> // memset

// wait:
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main() {

  char target[] = "./hello.py";

  enum PIPE_FILE_DESCRIPTERS {
    READ_FD = 0, WRITE_FD = 1
  };

  enum CONSTANTS {
    BUFFER_SIZE = 100
  };

  int parentToChild[2];
  int childToParent[2];
  pid_t pid;
  string dataReadFromChild;
  char buffer[BUFFER_SIZE + 1];
  memset(buffer,0x00,BUFFER_SIZE + 1);
  ssize_t readResult;
  int status;

  int retPipe1 =  pipe(parentToChild);
  int retPipe2 =  pipe(childToParent);

  switch (pid = fork()) {
  case -1:
    printf("Fork failed");
    exit(-1);

  case 0: /* Child will start scripts*/ 
  {
    // Bending stdin/out to the pipes?
    int retdup21 = dup2(parentToChild[READ_FD], STDIN_FILENO);
    int retdup22 = dup2(childToParent[WRITE_FD], STDOUT_FILENO);
    int retdup23 = dup2(childToParent[WRITE_FD], STDERR_FILENO);
    // Close in this Process the other sides of the pipe
    int retclose1 = close(parentToChild[WRITE_FD]);
    int retclose2 = close(childToParent[READ_FD]);

    int retexe = execlp( target ," ");              // warning not enough variable arguments to fit a sentinel [-Wformat=]

    printf("This line should never be reached!!!"); // why? what happens if execlp finishes?
    exit(-1);
    break;  // to make the compiler happy =)
  }
  default: /* Parent */
    cout << "Child " << pid << " process running..." << endl;

    // close the other ends of the pipe from the other process.
    int retdup21 = close(parentToChild[READ_FD]);
    int retdup22 = close(childToParent[WRITE_FD]);

    // readtry
    while (true) {
      switch (readResult = read(childToParent[READ_FD], buffer, 1)) // execution does not return from this function.
      {
      case 0: /* End-of-File, or non-blocking read. */
      {
        cout << "End of file reached..." << endl << "Data received was (" << dataReadFromChild.size() << "):" << endl
            << dataReadFromChild << endl;

        cout << "starting writing" << endl;
        char bufferW[] = "{\"AElement\":\"Something\"}\0";


               int writeResult = write(parentToChild[WRITE_FD],bufferW,sizeof(bufferW));
               int saveerrno = errno;

               if( -1 == writeResult)
               {
                 cout << "errno while writing: " << errno << std::endl;
                 if ( 9 == saveerrno )
                   cout << "Errno Bad File descriptor" << endl;
               }

               cout << "Write Result: " << writeResult << std::endl;

        int retWait = waitpid(pid, &status, 0);

        cout << endl << "Child exit staus is:  " << WEXITSTATUS(status) << endl << endl;

        exit(0);
      }
      case -1:
      {
        if ((errno == EINTR) || (errno == EAGAIN)) {
          errno = 0;
          break;
        } else {
          printf("read() failed");
          exit(-1);
        }
      }
      default:
        dataReadFromChild.append(buffer, readResult);
        printf("%s",buffer);
        memset(buffer,0x00,BUFFER_SIZE + 1);
        break;
      }
    } /* while ( true ) */
  } /* switch ( pid = fork() )*/
}

您的問題是緩沖輸出,未關閉的文件描述符以及使用EOF表示傳輸的一部分結束。 前兩個問題可以解決,但最后一個需要不同的方法。 以后再說。

一步步:

Python使用的是緩沖I / O,因此您可能想通過在第一個print語句之后添加sys.stdout.flush()行來強制Python刷新輸出。 現在, "Hello!\\n"被逐字符讀取。

但是隨后的下一個read阻塞,直到出現新字符或管道被關閉為止。 Python腳本的STDOUT仍然打開,C ++程序正在等待到達的內容,但是Python腳本本身也在等待一些輸入。 經典僵局。

您可能會丟棄python腳本中的最后一個打印,然后嘗試關閉其STDOUT。 由於read阻塞,直到所有引用管道寫入端的文件描述符都被關閉,因此在flush之后,您將不得不添加os.close(sys.stdout.fileno())os.close(sys.stdout.fileno())

但是仍然有有效的文件描述符引用該管道的寫入部分。 還記得C ++源代碼中的dup2嗎? 在這三行dup2行之后,仍然有parentToChild[READ_FD]childToParent[WRITE_FD]引用腳本STDIN和STDOUT。 因此,我們必須關閉它們。 添加close(parentToChild[READ_FD]); close(childToParent[WRITE_FD]); dup2之后。 現在,當Python腳本關閉STDOUT和STDERR時, read返回0

接下來,父級發送"{\\"AElement\\":\\"Something\\"}\\0"並到達waitpid ,當孩子退出時, waitpid返回。 但是孩子仍然在讀STDIN。 因此,您必須添加close(parentToChild[WRITE_FD]); waitpid之前。


現在是概念部分:在它返回0 (管道關閉)之前,您不能read() ),然后繼續從該關閉的管道讀取。 您的選擇:

  • 讀取一次,直到管道關閉。 沒有第二條消息。
  • 知道要讀多少書。 提前或通過解釋接收到的字節。
  • 監視兩個管道,例如使用poll(2)並動態決定是否要讀取或寫入。

順便說一句: execlp的參數是const char *file, const char *arg, ... ,其中arg, ...是通常的char *args[]arg[0] (!)開頭,以空指針結尾。 請將該行更改為int retexe = execlp(target, target, (char*) NULL);


#!/usr/bin/python2.7

import os
import sys

print "Hello!"
sys.stdout.flush()
os.close(sys.stdout.fileno())
os.close(sys.stderr.fileno())

data = sys.stdin.read()
with open("data_received_by_child.txt", "w") as fp:
    print >>fp, data

#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <iostream>

#include <sys/wait.h>
#include <unistd.h>

using namespace std;

int main()
{
    const char *target = "./hello.py";

    enum PIPE_FILE_DESCRIPTERS {
        READ_FD = 0, WRITE_FD = 1
    };

    /* Make pipes */
    int parentToChild[2]; /* Parent to child pipe */
    if (pipe(parentToChild) < 0)
    {
        perror("Can't make pipe");
        exit(1);
    }
    int childToParent[2]; /* Child to parent pipe */
    if (pipe(childToParent) < 0)
    {
        perror("Can't make pipe");
        exit(1);
    }

    /* Create a child to run command. */
    pid_t pid = fork();
    switch (pid)
    {
        case -1:
            perror("Can't fork");
            exit(1);

        case 0: /* Child */
            close(parentToChild[WRITE_FD]);
            close(childToParent[READ_FD]);
            dup2(parentToChild[READ_FD], STDIN_FILENO);
            dup2(childToParent[WRITE_FD], STDOUT_FILENO);
            close(parentToChild[READ_FD]);
            close(childToParent[WRITE_FD]);
            execlp(target, target, (char *) NULL);
            perror("Can't execute target");
            exit(1);

        default: /* Parent */
            close(parentToChild[READ_FD]);
            close(childToParent[WRITE_FD]);
            cout << "Child " << pid << " process running..." << endl;
    }

    /* Read data from child */
    string dataReadFromChild;
    char ch;
    int rc;
    while ((rc = read(childToParent[READ_FD], &ch, 1)) != 0)
    {
        if (rc == -1) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                continue;
            }
            perror("read() failed");
            exit(-1);
        }
        dataReadFromChild += ch;
    }
    close(childToParent[READ_FD]);
    cout << "End of file reached..." << endl;
    cout << "Data received was (" << dataReadFromChild.size() << "):" << endl;
    cout << dataReadFromChild << endl;

    /* Write data to child */
    cout << "starting writing" << endl;
    const char bufferW[] = "{\"AElement\":\"Something\"}\0";
    while (true) {
        int rc = write(parentToChild[WRITE_FD], bufferW, sizeof(bufferW));
        if (rc == -1) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                continue;
            }
            perror("write() failed");
            exit(-1);
        }
        break;
    }
    close(parentToChild[WRITE_FD]);

    /* Wait for child to exit */
    int status;
    int retWait = waitpid(pid, &status, 0);
    cout << endl << "Child exit status is:  " << WEXITSTATUS(status) << endl << endl;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM