Страницы

Поиск по вопросам

среда, 29 января 2020 г.

Контроллировать I/O другой программы на C/C++

#cpp #linux #c


Здравствуйте. Возможно тема немного сбивает с толку, по этому поясню проще: мне нужна
программа на С/С++, которая сможет запустить переданную ей в аргументы другую программу,
а потом дать ей кое-что на вход (input), подождать пока она (переданная ей программа)
сделает свое и получить её вывод (output). И ещё: программа, которую я передам в аргументы
моей программе-оболочке -- скрипт на Python 3, может это важная деталь. Программа будет
под Linux. Собственно мне нужно узнать через какие функции можно это сделать (я имею
ввиду делать ввод в программу и получать её вывод). 
    


Ответы

Ответ 1



Посмотрите в сторону popen - например, здесь или здесь - это вам не поможет?

Ответ 2



В простейшем случае можно сделать 2 пайпа (pipe), через первый будете писать скрипту, а через второй читать от него, потом создать процесс (fork), в котором можно переключить (dup2) дескрипторы пайпа на stdin и stdout скрипта и запустить его (например, execl). Вот примитивный пример: #include #include #include #include #include #include #include #include int main (int ac, char *av[]) { if (ac < 2) errx(EX_USAGE, "Usage: %s cmd [args]\n", av[0]); pid_t pid, cmd; int pin[2], pout[2]; // пара пайпов для обмена со скриптом if (pipe(pin) || pipe(pout)) err(EX_OSERR, "pipes"); if (!(cmd = fork())) { // запуск скрипта с аргументами close(pin[1]); close(pout[0]); dup2(pin[0], 0); // заменим stdin скрипта на наш ввод dup2(pout[1], 1); // перекинем stdout скрипта на наc // stderr скрипта остался тем же, что и у нас execvp(av[1], av + 1); err(EX_UNAVAILABLE, "exec %s", av[1]); } if (cmd == -1) err(EX_OSERR, "fork"); // эти дескрипторы более не нужны close(pin[0]); close(pout[1]); char *str = 0; size_t ssize = 0; ssize_t l; // для примера читаем stdin и передаем в скрипт while ((l = getline(&str, &ssize, stdin)) > 0) if (write(pin[1], str, l) != l) err(EX_IOERR, "write pipe"); close(pin[1]); // пошлем EOF на stdin скрипта // читаем вывод скрипта и для примера печатаем его FILE *progout = fdopen(pout[0], "r"); while (getline(&str, &ssize, progout) > 0) puts(str); free(str); int status; errno = 0; if ((pid = wait(&status)) != cmd) { if (errno) err(EX_SOFTWARE, "wait"); else errx(EX_SOFTWARE, "wait unexpected PID %ld (waited %ld)", (long)pid, (long)cmd); } if (WIFEXITED(status)) printf("%s exit code %d\n", av[1], WEXITSTATUS(status)); else printf("%s terminated by %d\n", av[1], WTERMSIG(status)); return puts("End") == EOF; } Тут могут быть "подводные камни" (особенно если захотите "интерактивной работы"), поскольку FILE * с пайпом (в отличии от терминала) буферизуется (и вывод fprintf/fputs не будет реально выводиться в пайп по строкам), а также пайпы имеют ограниченный размер, что может привести к взаимной блокировке процессов. Например, Вы пишете скрипту, он возвращает свои результаты через пайп, и если вы этот пайп не читаете, то скрипт "повиснет" на своей операции записи (пайп заполнен, а его никто не читает). Для корректного решения такой ситуации надо читать ответы скрипта по мере их появления, для чего посмотрите на poll или select. А также для решения подобных задач полезно посмотреть на псевдотерминалы (проще всего начать с вызова forkpty, ну и не забудьте про все SEE_ALSO :-)).

Комментариев нет:

Отправить комментарий