Capturer stdout à partir d’une commande system () de manière optimale

J’essaie de démarrer une application externe via system() – par exemple, system("ls") . Je voudrais capturer sa sortie comme il arrive afin que je puisse l’envoyer à une autre fonction pour un traitement ultérieur. Quelle est la meilleure façon de le faire en C / C ++?

Du manuel popen:

 #include  FILE *popen(const char *command, const char *type); int pclose(FILE *stream); 

Essayez la fonction popen (). Il exécute une commande, comme system (), mais dirige la sortie dans un nouveau fichier. Un pointeur sur le stream est renvoyé.

  FILE *lsofFile_p = popen("lsof", "r"); if (!lsofFile_p) { return -1; } char buffer[1024]; char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p); pclose(lsofFile_p); 

EDIT: question mal interprétée comme voulant passer la sortie à un autre programme, pas une autre fonction. popen () est presque certainement ce que vous voulez.

Le système vous donne un access complet au shell. Si vous souhaitez continuer à l’utiliser, vous pouvez redirect sa sortie vers un fichier temporaire, par système (“ls> tempfile.txt”), mais choisir un fichier temporaire sécurisé est un problème. Ou, vous pouvez même le redirect via un autre programme: system (“ls | otherprogram”);

Certains peuvent recommander la commande popen (). C’est ce que vous voulez si vous pouvez traiter le résultat vous-même:

 FILE *output = popen("ls", "r"); 

qui vous donnera un pointeur FILE que vous pourrez lire avec la sortie de la commande.

Vous pouvez également utiliser l’appel pipe () pour créer une connexion en combinaison avec fork () pour créer de nouveaux processus, dup2 () pour en modifier l’entrée et la sortie standard, exec () pour exécuter les nouveaux programmes et wait () dans le programme principal pour les attendre. Cela ne fait que mettre en place le pipeline comme le ferait le shell. Voir la page de manuel pipe () pour plus de détails et un exemple.

Les fonctions popen() et autres ne redirigent pas stderr et autres; J’ai écrit popen3() pour cela.

Voici une version bowdlerized de mon popen3 ():

 int popen3(int fd[3],const char **const cmd) { int i, e; int p[3][2]; pid_t pid; // set all the FDs to invalid for(i=0; i<3; i++) p[i][0] = p[i][1] = -1; // create the pipes for(int i=0; i<3; i++) if(pipe(p[i])) goto error; // and fork pid = fork(); if(-1 == pid) goto error; // in the parent? if(pid) { // parent fd[STDIN_FILENO] = p[STDIN_FILENO][1]; close(p[STDIN_FILENO][0]); fd[STDOUT_FILENO] = p[STDOUT_FILENO][0]; close(p[STDOUT_FILENO][1]); fd[STDERR_FILENO] = p[STDERR_FILENO][0]; close(p[STDERR_FILENO][1]); // success return 0; } else { // child dup2(p[STDIN_FILENO][0],STDIN_FILENO); close(p[STDIN_FILENO][1]); dup2(p[STDOUT_FILENO][1],STDOUT_FILENO); close(p[STDOUT_FILENO][0]); dup2(p[STDERR_FILENO][1],STDERR_FILENO); close(p[STDERR_FILENO][0]); // here we try and run it execv(*cmd,const_cast(cmd)); // if we are there, then we failed to launch our program perror("Could not launch"); fprintf(stderr," \"%s\"\n",*cmd); _exit(EXIT_FAILURE); } // preserve original error e = errno; for(i=0; i<3; i++) { close(p[i][0]); close(p[i][1]); } errno = e; return -1; } 

Le moyen le plus efficace est d’utiliser directement le descripteur de fichier stdout , en contournant le stream FILE :

 pid_t popen2(const char *command, int * infp, int * outfp) { int p_stdin[2], p_stdout[2]; pid_t pid; if (pipe(p_stdin) == -1) return -1; if (pipe(p_stdout) == -1) { close(p_stdin[0]); close(p_stdin[1]); return -1; } pid = fork(); if (pid < 0) { close(p_stdin[0]); close(p_stdin[1]); close(p_stdout[0]); close(p_stdout[1]); return pid; } else if (pid == 0) { close(p_stdin[1]); dup2(p_stdin[0], 0); close(p_stdout[0]); dup2(p_stdout[1], 1); dup2(::open("/dev/null", O_WRONLY), 2); /// Close all other descriptors for the safety sake. for (int i = 3; i < 4096; ++i) { ::close(i); } setsid(); execl("/bin/sh", "sh", "-c", command, NULL); _exit(1); } close(p_stdin[0]); close(p_stdout[1]); if (infp == NULL) { close(p_stdin[1]); } else { *infp = p_stdin[1]; } if (outfp == NULL) { close(p_stdout[0]); } else { *outfp = p_stdout[0]; } return pid; } 

Pour lire la sortie de l' enfant, utilisez popen2() comme ceci:

 int child_stdout = -1; pid_t child_pid = popen2("ls", 0, &child_stdout); if (!child_pid) { handle_error(); } char buff[128]; ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 

A la fois écrire et lire:

 int child_stdin = -1; int child_stdout = -1; pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout); if (!child_pid) { handle_error(); } const char text = "1\n2\n123\n3"; ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1); char buff[128]; ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 

Les fonctions popen() et pclose() peuvent être ce que vous recherchez.

Regardez le manuel de glibc pour un exemple.

Sous Windows, au lieu d’utiliser system (), utilisez CreateProcess, redirigez la sortie vers un canal et connectez-vous au canal.

Je suppose que cela est également possible de manière POSIX?

En fait, je viens de vérifier et:

  1. popen est problématique, car le processus est fourchu. Donc, si vous avez besoin d’attendre l’exécution de la commande shell, vous risquez de la manquer. Dans mon cas, mon programme a été fermé avant même que le pipe ne fasse son travail.

  2. J’ai fini par utiliser l’appel système avec la commande tar sur Linux. La valeur de retour du système était le résultat de tar .

Donc: si vous avez besoin de la valeur de retour , alors non seulement il n’y a pas besoin d’utiliser popen, cela ne fera probablement pas ce que vous voulez.

Dans cette page: capture_the_output_of_a_child_process_in_c décrit les limitations de l’utilisation de popen par rapport à l’utilisation de l’approche fork / exec / dup2 / STDOUT_FILENO.

J’ai du mal à capturer la sortie de tshark avec popen .

Et je suppose que cette limitation pourrait être mon problème:

Il renvoie un stream stdio par opposition à un descripteur de fichier brut, ce qui ne convient pas pour gérer la sortie de manière asynchrone.

Je reviendrai sur cette réponse si j’ai une solution avec l’autre approche.

Je ne suis pas tout à fait certain que ce soit possible dans le standard C, car deux processus différents ne partagent généralement pas l’espace mémoire. Le moyen le plus simple de le faire serait de redirect la sortie du deuxième programme vers un fichier texte (nom_programme> textfile.txt), puis de relire ce fichier texte pour traitement. Cependant, ce n’est peut-être pas le meilleur moyen.