Comment lire les résultats de l'appel system() en C++?
J'utilise le code suivant pour essayer de lire les résultats d'une df
de commande sous Linux à l'aide de popen
.
#include <iostream> //file and std I/O functions
int main(int argc, char** argv) {
FILE* fp;
char * buffer;
long bufSize;
size_t ret_code;
fp = popen("df", "r");
if(fp == NULL) { //head off errors reading the results
std::cerr << "Could not execute command: df" << std::endl;
exit(1);
}
//get the size of the results
fseek(fp, 0, SEEK_END);
bufSize = ftell(fp);
rewind(fp);
//allocate the memory to contain the results
buffer = (char*)malloc( sizeof(char) * bufSize );
if(buffer == NULL) {
std::cerr << "Memory error." << std::endl;
exit(2);
}
//read the results into the buffer
ret_code = fread(buffer, 1, sizeof(buffer), fp);
if(ret_code != bufSize) {
std::cerr << "Error reading output." << std::endl;
exit(3);
}
//print the results
std::cout << buffer << std::endl;
//clean up
pclose(fp);
free(buffer);
return (EXIT_SUCCESS);
}
Ce code me donne une "erreur de Mémoire" avec un statut de sortie de '2', afin que je puisse voir où il ne fonctionne pas, je ne comprends pas pourquoi.
J'ai mis cet ensemble à partir de l'exemple de code que j'ai trouvé sur Ubuntu Forums et Référence C++ , donc je ne suis pas marié avec elle. Si quelqu'un peut proposer une meilleure façon de lire les résultats d'un appel system (), je suis ouvert à de nouvelles idées.
MODIFIER à l'original: Bon, bufSize
est à venir négatif, et maintenant je comprends pourquoi. Vous ne pouvez pas accéder de manière aléatoire à un tuyau, comme je l'ai naïvement tenté de le faire.
Je ne peux pas être la première personne à essayer de le faire. Quelqu'un peut-il donner (ou m'indiquer un exemple de comment lire les résultats de l'appel system() dans une variable en C++?
- 'Appel système" a un sens très précis - voir <en.wikipedia.org/wiki/System_call>. Ce que vous essayez de faire, pour récupérer la sortie d'un autre programme (je ne suis pas sûr de ce que le terme technique pour cela est hors de ma tête).
- Merci. J'ai édité la question pour essayer de clarifier.
Vous devez vous connecter pour publier un commentaire.
Pourquoi
std::malloc()
échec?La raison évidente est "parce que
std::ftell()
retourné négatif signé nombre, qui a ensuite été traitée comme un énorme nombre non signé".Selon la documentation,
std::ftell()
retourne -1 en cas d'échec. Une raison évidente, il ne pourrait pas, c'est que vous ne pouvez pas chercher dans les tubes et les FIFO.Il n'y a pas de fuite; vous ne pouvez pas connaître la longueur de la sortie de la commande sans le lire, et vous ne pouvez lire qu'une seule fois. Vous devez le lire en morceaux, soit la croissance de votre tampon en tant que de besoin ou de l'analyse à la volée.
Mais, bien sûr, vous pouvez simplement éviter de l'ensemble de la question directement à l'aide de l'appel système
df
utilise probablement pour obtenir ses informations:statvfs()
.Vous êtes de prendre tout cela trop dur. popen(3) retourne régulièrement vieux
FILE *
pour une pipe standard de fichier, qui est-à-dire, newline cessé. Vous pouvez le lire avec une très grande efficacité en utilisant fgets(3) comme en C:En C++ c'est encore plus facile --
Il y a un peu plus d'erreur de manipulation il y a, mais c'est l'idée. Le point est que vous devez traiter les
FILE *
de popen comme toutFILE *
, et de le lire ligne par ligne.Je ne suis pas sûr que vous pouvez fseek/ftell tuyau de flux comme ça.
Avez-vous vérifié la valeur de bufSize ? L'une des raisons malloc être en défaut est incroyablement tampons de taille.
(Une remarque sur la terminologie: "appel système" d'Unix et de Linux en général fait référence à l'appel d'une fonction noyau à partir de l'espace utilisateur code. Se référant à elle que "les résultats d'une
system()
appel" ou "les résultats d'unesystem(3)
appel" serait plus clair, mais il serait probablement préférable de dire simplement "la capture de la sortie d'un processus.")De toute façon, vous pouvez lire un processus de sortie du tout comme vous pouvez le lire n'importe quel autre fichier. Plus précisément:
pipe()
,fork()
, etexec()
. Cela vous donne un descripteur de fichier, vous pouvez utiliser une boucle pourread()
à partir du descripteur de fichier dans un buffer etclose()
le descripteur de fichier une fois que vous avez terminé. C'est le plus bas niveau de l'option et vous donne le plus de contrôle.popen()
, comme vous le faites. Cela vous donne un flux de fichier. Dans une boucle, vous pouvez lire à l'aide du flux dans une variable temporaire ou de la mémoire tampon à l'aide defread()
,fgets()
, oufgetc()
, comme Zarawesome la réponse de la montre, alors le processus de la mémoire tampon ou ajouter à une chaîne C++.popen()
, puis utilisez la non standard __gnu_cxx::stdio_filebuf à étirable, puis de créer unstd::istream
de lastdio_filebuf
et de le traiter comme n'importe quel autre C++ flux. C'est le plus C++comme approche. Voici partie 1 et partie 2 d'un exemple de cette approche.Merci à tous ceux qui ont pris le temps de répondre. Un collègue m'a signalé la ostringstream classe. Voici un exemple de code qui fait essentiellement ce que je tente de faire dans la question d'origine.
Pour répondre à la question de la mise à jour:
Serait-ce suffisant?
Première chose à vérifier est la valeur de bufSize - si ce qui arrive à être <= 0, les chances sont que malloc renvoie une valeur NULL comme vous essayez d'allouer un tampon de taille 0 à ce stade.
Une autre solution serait de demander à malloc pour vous fournir avec un buffer de la taille (bufSize + n) avec n >= 1, ce qui devrait contourner ce problème particulier.
Cela dit, le code que vous avez posté est de la pure C, pas du C++, donc y compris est exagérer un peu.
vérifier votre bufSize.
ftell
pouvez renvoie -1 en cas d'erreur, et cela peut conduire à nonallocation par malloc avec tampon ayant une valeur NULL.La raison de la
ftell
l'échec est, en raison de la popen. Vous ne pouvez pas rechercher des tuyaux.Les tuyaux ne sont pas en accès aléatoire. Ils sont séquentielles, ce qui signifie qu'une fois que vous lisez un octet, le tuyau ne va pas à l'envoyer à nouveau. Ce qui signifie, évidemment, vous ne pouvez pas rembobiner.
Si vous voulez juste à la sortie de la données à l'utilisateur, vous pouvez simplement faire quelque chose comme:
Cela tirera octets de la df pipe, un par un, et la pompe directement dans la sortie.
Maintenant, si vous voulez accéder à la df de sortie ensemble, vous pouvez pipe dans un fichier et lire ce fichier, ou concaténer la sortie dans une construction comme une Chaîne C++.