J'essaie de démarrer une application externe via system()
- par exemple, system("ls")
. Je voudrais capturer sa sortie comme cela arrive afin que je puisse l'envoyer à une autre fonction pour un traitement ultérieur. Quelle est la meilleure façon de faire cela en C/C++?Capture stdout à partir d'une commande system() de manière optimale
Répondre
Je ne suis pas entièrement certain que ce soit possible dans la norme C, car deux processus différents ne partagent généralement pas l'espace mémoire. La façon la plus simple de le faire serait de rediriger le second programme vers un fichier texte (nom du programme> textfile.txt), puis de le lire à nouveau pour le traitement. Cependant, ce n'est peut-être pas le meilleur moyen.
EDIT: question mal interprétée comme voulant transmettre la sortie à un autre programme, pas à une autre fonction. popen() est presque certainement ce que vous voulez.
Le système vous donne un accès complet au shell. Si vous voulez continuer à l'utiliser, vous pouvez rediriger la sortie vers un fichier temporaire, par le système ("ls> tempfile.txt"), mais choisir un fichier temporaire sécurisé est un problème. Ou, vous pouvez même le rediriger à travers un autre programme: system ("ls | otherprogram");
Certains peuvent recommander la commande popen(). C'est ce que vous voulez si vous pouvez traiter vous-même sortie:
FILE *output = popen("ls", "r");
qui vous donnera un pointeur de fichier que vous pouvez lire avec la sortie de la commande sur elle.
Vous pouvez également utiliser l'appel pipe() pour créer une connexion avec fork() pour créer de nouveaux processus, dup2() pour en changer l'entrée et la sortie standard, exec() pour exécuter les nouveaux programmes, et wait() dans le programme principal pour les attendre. Ceci est juste en train de configurer le pipeline comme le ferait le shell. Voir la page de manuel pipe() pour plus de détails et 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 c'est aussi possible de quelque manière POSIX?
A partir du manuel popen:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
FICHIER * stream = popen (commande const char *, type const char *); – Arpith
Je voudrais ajouter que cela ne fonctionnera que sur les systèmes Posix. Sur ma version de Windows 7 il n'y a pas de fonction popen mais il y a un _popen qui marche. – user1505520
Essayez la fonction popen(). Il exécute une commande, comme system(), mais dirige la sortie dans un nouveau fichier. Un pointeur sur le flux 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);
Les fonctions popen()
et pclose()
pourrait être ce que vous cherchez. Regardez un glibc manual pour un exemple.
Les fonctions popen()
et telles ne redirigent pas stderr et tel; J'ai écrit popen3()
à cette fin.
En fait, je viens de vérifier, et:
popen est problématique, parce que le processus est en forme de fourche. Donc, si vous devez attendre l'exécution de la commande shell, vous risquez de la manquer. Dans mon cas, mon programme s'est fermé avant même que le tuyau ne fasse son travail.
J'ai fini par utiliser système appel avec commande tar sur linux. La valeur de retour de système était le résultat de tar.
Alors: si vous avez besoin de la valeur de retour, alors non pas seulement il pas nécessaire d'utiliser popen, il ne sera probablement pas faire ce que vous voulez.
La question demande de capturer la * sortie *, pas le * code de sortie *. Donc, je ne pense pas que le problème que vous mentionnez se pose. –
c'est la raison d'être de la famille d'attente (...) des appels système. Ils attendent que le processus de votre enfant se termine, de sorte que vous pouvez obtenir sa sortie. "man -s2 wait" –
Dans cette page: capture_the_output_of_a_child_process_in_c décrit les limitations d'utilisation de popen par rapport à l'utilisation de l'approche fork/exec/dup2/STDOUT_FILENO.
J'ai des problèmes capturing tshark output with popen.
Et je suppose que cette limitation pourrait être mon problème:
Il retourne un flux stdio par opposition à un descripteur de fichier brut, qui ne convient pas à gérer la sortie de manière asynchrone.
Je reviendrai à cette réponse si j'ai une solution avec l'autre approche.
La façon la plus efficace est d'utiliser le descripteur de fichier stdout
directement, sans passer par flux 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 enfant utilisation 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));
Pour les deux écriture 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));
Est-ce correct dup2 (:: open ("/ dev/null", O_RDONLY), 2) ;? Il semble plus logique dup2 (:: open ("/ dev/null", O_WRONLY), 2); –
@MelVisoMartinez, yup, serait plus valable. – GreenScape
Que voulez-vous dire par optimalement? De ma réponse, je dirais que de manière optimale peut dépendre de chaque situation. L'approche fork/exec/dup2/STDOUT_FILENO peut convenir à des cas particuliers. – nephewtom