If you want to exchange messages with a script from a C / C ++ program in Linux, just like you can do it sitting at a terminal (i.e., if the script reads stdin and writes to stdout), you can use this function
#include <stdio.h> #include <stdlib.h> #include <termios.h> #include <unistd.h> #include <pty.h> FILE * pty_execvp (char *argv[], pid_t *child) { int pty; struct termios t; cfmakeraw(&t); if (!(*child = forkpty(&pty, 0, &t, 0))) { execvp(argv[0], argv); fprintf(stderr, "exec %s: %m\n", argv[0]); exit(127); } else if (*child == -1) return 0; return fdopen(pty, "r+"); }
Similar to popen, it returns the FILE * associated with a pseudo-terminal, on which a script (program) with specified arguments is run in a new process.
The name of the file with the script and its arguments are passed as if to the execvp function (only the first argument of the script is taken for the name of the file being run).
The address in the child argument is the pid of this process.
You need to compile with the -lutil key (see man forkpty ).
Here is a (somewhat cumbersome and artificial) example of its use.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <poll.h> FILE *pty_execvp (char *argv[], pid_t *child); int fd_out (int fd, FILE *out, int tmout) { struct pollfd fds = {fd, POLLIN}; char buf[1024]; int l, s = 0; while (poll(&fds, 1, tmout) > 0) { if ((l = read(fd, buf, 1024)) > 0) { s += l; fwrite(buf, 1, l, out); } else return -1; } return s; } int main (int ac, char *av[]) { char *line = 0; size_t lsz; const char *cmd = "ls -l *.txt"; FILE *in = popen(cmd, "r"); if (!in) perror("popen"), exit(2); const char *pcmd[] = { "grep", "20", 0 }; pid_t cpid; FILE *pty = pty_execvp((char **)pcmd, &cpid); if (pty) printf("run %s pid %ld\n", pcmd[0], (long)cpid); else exit(fputs("pty-exec error", stderr)); while (getline(&line, &lsz, in) > 0) { fputs(line, pty); // printf("> %s", line); if (fd_out(fileno(pty), stdout, 0) < 0) { perror("fd_out"); break; } } while (fd_out(fileno(pty), stdout, 1) > 0); int rc; rc = pclose(in); printf("%s rc = %d\n", WIFEXITED(rc) ? "exit" : "terminated", WEXITSTATUS(rc)); fclose(pty); pid_t fin = wait(&rc); if (WIFEXITED(rc)) printf("Exit pid:%ld %d\n", (long)fin, WEXITSTATUS(rc)); else if (WIFSIGNALED(rc)) printf("Signal pid:%ld %d\n", (long)fin, WTERMSIG(rc)); else printf("??? pid:%ld %d\n", (long)fin, rc); return puts("End") == EOF; }
This example gives the same result as ls -l *.txt | grep 20 ls -l *.txt | grep 20 and demonstrates how to use popen with pty_exec for the case when we do not know in advance what the response from grep to the transmitted string will be.
Note the timeout in the fd_out() call at the end of the program.