2016-04-17 1 views
0

Я хочу вызвать скрипт python из моего кода на C++. Сценарий python выглядит так:C++ запускает скрипт (bash, python ...) и использует stdin/stdout для datatransfare [linux]

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

Код C++ из другого вопроса из переполнения стека. Как это должно работать:

  • Создание пары труб.

  • Создание дочернего процесса с fork().

  • ребенок изгибает свои трубы до уровня stdin/stdout. Закрытие других концов и запуск сценария.

  • прослушивает трубы read(), принимает ввод. И после этого, , отправив сообщение write().

Программа не возвращается из отцов линии, когда switch (readResult = read(childToPa... вводится.

Я также не знаю, выполняет ли эта письменная часть свою работу. Является ли это многообещающей идеей делать это так, или есть другие рабочие возможности? thx!

Это выглядит следующим образом:

// 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())*/ 
} 
+0

Во-первых, как это важно, что вы запустить код Python в другой программе? Ответ: Это не так. Таким образом, вы можете легко уменьшить свой код и вопрос, приблизившись к минимальному примеру. Однако, даже тогда, мне интересно, откуда у вас возникла идея подхода? Это не ново, не уникально и на самом деле хорошо установлено. Таким образом, должно быть тривиально найти существующие рабочие примеры! –

+0

@ Ульрих Экхардт Сборка имеет ядро ​​C++. Это получает вход для работы. Ядро готовит эту работу, обновляет базу данных и т. Д. После этого работа будет выполняться с помощью python. Ядро останется на будущее. Но вид работы, который предстоит сделать, изменится в будущем. Поэтому мы искали простой способ сделать это. Введите имя скриптового файла с API -> C++ prepare -> Python. Есть ли лучший способ сделать это? Я программист, и я не предпочитаю использовать этот хак. Поскольку эти сценарии должны быть вызваны друг другом, был предложен метод stdin/put. Лучшие идеи? Thx =) –

ответ

1

Ваши проблемы буферизацией вывода, незакрытые дескрипторов файлов и с помощью EOF, чтобы сигнализировать конец части передачи. Первые две проблемы могут быть решены, но последний нужен другой подход. Об этом позже.

Шаг за шагом:

Питон с использованием буферизацией ввода/вывода, так что вы можете заставить Python удалить вывод, добавив строку sys.stdout.flush() после первого оператора печати. Теперь "Hello!\n" читается символом.

Но затем следующие read блокируются до тех пор, пока не появится новый символ или труба не будет закрыта. STDOUT скрипта Python все еще открыт, программа на C++ ждет чего-то, но сам скрипт Python тоже ждет ввода. Классический тупик.

Вы можете удалить последний отпечаток в скрипте python и попытаться закрыть его STDOUT. Поскольку read блокирует до тех пор, пока не будут закрыты все дескрипторы файлов, ссылающиеся на конец записи канала, вам необходимо будет добавить os.close(sys.stdout.fileno()) и os.close(sys.stdout.fileno()) после flush.

Но все еще существуют действительные файловые записи, ссылающиеся на часть записи этого канала. Помните dup2 в источнике C++? После этих трех линий dup2 есть еще parentToChild[READ_FD] и childToParent[WRITE_FD], ссылающиеся на скрипты STDIN и STDOUT. Поэтому мы должны закрыть их. Добавить close(parentToChild[READ_FD]); и close(childToParent[WRITE_FD]); сразу после dup2 s. Теперь read возвращает 0, когда скрипт Python закрывает STDOUT и STDERR.

Затем родитель отправляет "{\"AElement\":\"Something\"}\0" и достигает waitpid, который возвращается, когда ребенок завершает работу. Но ребенок все еще читает от STDIN. Поэтому вы должны добавить close(parentToChild[WRITE_FD]); до waitpid.


Теперь для концептуальной части: Вы не можете read(), пока она не возвращает 0 (труба закрыта), а затем продолжить чтение с этой закрытой трубе. Ваш выбор:

  • Прочитано один раз, пока труба не будет закрыта. Нет второго сообщения.
  • Знайте, сколько читать. Либо заранее, либо интерпретируя полученные байты.
  • Мониторинг обеих труб, например. с 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; 
} 
+0

Благодарим вас за эту информацию и указав каждую ошибку, которую я сделал/скопировал. Я займусь этим через пару дней. Чтобы получить дополнительную информацию, обратитесь к этой теме. Программные средства связи, трубы, запуск внешних программ и зомби. Можете ли вы назвать меня хорошей книгой или несколькими ключевыми словами, с чего начать мой поиск? –

+0

Лучшая книга по уровню POSIX и программированию Linux, с которыми я столкнулся, - это «Интерфейс программирования Linux» Майкла Керриска. Мне также понравилось «Руководство программиста POSIX» Дональда Левина (опубликовано в 1991 году, теперь доступно бесплатно). Но мои самые важные ссылки - это справочные страницы. – Yurim

+0

** Спасибо! ** Для первой итерации в тему мне нравятся книги. Чем вы можете наслаждаться man-страницами для дальнейших вопросов. –

Смежные вопросы