Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

Sujet : Executer une commande [ Linux / Autre ] (ffomnislash)

vendredi 25 août 2006 à 22:50:34 | Executer une commande

ffomnislash


hello ;)

J'ai un programme ecrit en c++/gtkmm dans lequel je veut executer une commande et surtout recupérer son resultat au fur et a mesure.

J'ai une solution qui consite a
- executer la commande avec std::system() avec redirection du resultat dans un fichier
- en paralelle une boucle qui verifie si des nouvelles lignes sont presentes dans le fichier, si c'est le cas je les lis.

Je me demandais si il existait un autre solution peut etre plus propre, comme une librairie un peu plus puissant que std::system()

merci ;)


vendredi 25 août 2006 à 23:04:52 | Re : Executer une commande

vecchio56

Administrateur CodeS-SourceS
Sous Linux il y a la fonction pipe pour faire ca

_____________________________________
Un éditeur de ressources gratuit pour Windows


vendredi 25 août 2006 à 23:19:24 | Re : Executer une commande

ffomnislash



soit tu ne comprend pas ma question, soit je ne comprend pas ta réponse :D

Je connait la commande pipe, je l'utilise souvent pour faire des grep sur un resultat.
Mais je ne voit pas comment je pourrait l'utiliser pour résoudre mon probleme.

samedi 26 août 2006 à 00:30:59 | Re : Executer une commande

Bel0



Comme vecchio l'a si bien dit, il faut utiliser pipe ... enfin presque :)

Il faut en fait comprendre ce qui se passe quand on utilise un pipe dans un shell (C'est en fait une fonctionalité du shell). L'utilisation du pipe indique au shell qu'il doit rediriger la sortie du premier programme vers l'entrée du second. Redirection implique une modification des handles standard input et standard output (puisqu'on veut récupérer le résultat en sortie).

Pour ce faire, on utilise donc un pipe (ou un unix socket, rien à voir avec les sockets ip) qui agit comme un buffer entre les deux programmes.

J'ai déjà écrit ce genre de code pour un serveur web lors de l'exécution des cgi et de la récupération des résultats de l'exécution (si tu ne vois pas le lien, ne cherche pas ce n'est pas grave :p).

Je copie la partie du code implémente cette fonctionalité. Je n'ai pas mis toutes les fonctions utilisées mais leurs noms est assez explicite pour que tu puisses les recoder.

Argument de la fonction:
1) tableau de string formant la ligne de commande
2) tableau de string pour les variables d'environnement
3) pointeur vers un entier dans lequel sera stocké le "file descriptor" à utiliser pour lire le stdout et écrire dans le stdin du programme (simplement avec read et write comme s'il s'agissait d'un fichier).

Le principe de la fonction est de créer une paire de socket déjà connecté, de faire un fork (les file descriptors pour le deux bouts du tunnel sont toujours valides), de fermer un des deux bouts selon qu'on est dans le parent où dans le fils. Pour le processus parent le travail s'arrête là. Dans le processus fils, il faut encore dédoubler le bout du "tunnel" en stdin et stdout. Il faut enfin faire un exec pour lancer le programme à exécuter. La fonction exec ne réouvre pas le flux d'entrée et de sortie. De cette façon, nos modifications de stdin et stdout seront conservées. Assez de blabla, maintenant le code :P

int
io_popen(char *argv[], char *envp[], int *srv_child)
{
    int sockpair[2];
    int child_srv;
    
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1)
    {
        log_error("socketpair failed: %s", strerror(errno));
        return -1;
    }
    
    *srv_child = sockpair[0];
    child_srv = sockpair[1];
    
    int retval = fork();
    if(retval < 0)
    {
        /* parent - fork has failed */
        log_error("fork failed: %s", strerror(errno));
        io_close(child_srv);
        io_close(*srv_child);
        return -1;
    }
    else if(retval > 0)
    {
        /* parent - close the file descriptor used by the child */
        io_close(child_srv);
        return 0;
    }
    
    /*---------------*
     | Child process |
     *---------------*/
    
    /* close socket used by parent */
    io_close(*srv_child);
    
    /* logs shouldn't be accessed by cgi */
    log_close();
    
    /* close stdin and stdout */
    io_close(STDIN_FILENO);
    io_close(STDOUT_FILENO);
    
    /* duplicate input and output to stdin and stdout */
    if (dup2(child_srv, STDIN_FILENO) != STDIN_FILENO ||
        dup2(child_srv, STDOUT_FILENO) != STDOUT_FILENO)
    {
        io_close(child_srv);
        debug_msg(ERROR, "dup2 failed: %s", strerror(errno));
        _exit(EXIT_FAILURE);
    }
    
    execve(argv[0], argv, envp);
    
    /* execve doesn't return on success */
    if (errno == ENOEXEC || errno == EACCES)
        debug_msg(ERROR, "CGI file not executable");
    else
        debug_msg(ERROR, "execve failed: %s", strerror(errno));
    
    /* prevent the call to the function registered with atexit */
    _exit(EXIT_FAILURE);
}

Si tu as des questions sur le code ou sur mes explications (légèrement foireuse ?), n'hésite j'essayerais de faire mieux.

Belo

samedi 26 août 2006 à 00:34:19 | Re : Executer une commande

Bel0

Mini question dans ce thread: est-ce normal que l'espace entre les lignes de code soit si grand (avec firefox et ie) ? J'ai utilisé le type "Formatte" dans le "textbox évolué".

samedi 26 août 2006 à 12:45:46 | Re : Executer une commande

ffomnislash

merci :D
J'ai essayé de comprendre l'aglo et je pense avoir réussi ^^
Le revoici avec des commentaires supplémentaires, si tu pouvais regarder ;)
int io_popen(char *argv[], char *envp[], int *srv_child)
{
    int sockpair[2];
    int child_srv;

    //on commence a créer socketpair.
    //Ceci va permettre d'avoir un tunelle entre srv_child et child_srv afin d'avoir un lien entre les 2 processus
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1)
    {
        log_error("socketpair failed: %s", strerror(errno));
        return -1;
    }

    *srv_child = sockpair[0];
    child_srv = sockpair[1];

   
    //on fait le, fork et on ferme le socketpair qui n'est pas utilisé par le processus (chaque processus en garde 1)
    int retval = fork();
    if(retval < 0)
    {
        /* parent - fork has failed */
        log_error("fork failed: %s", strerror(errno));
        io_close(child_srv);
        io_close(*srv_child);
        return -1;
    }
    else if(retval > 0)
    {
        /* parent - close the file descriptor used by the child */
        io_close(child_srv);
        return 0;
    }

    /*---------------*

     | Child process |

     *---------------*/

    /* close socket used by parent */
    io_close(*srv_child);

   
    //ca je comprend pas , c'est lié à ton utilisation du code je suppose.
    /* logs shouldn't be accessed by cgi */
    log_close();

   

    //on ferme les entrées/sorties standards car on les redirige apres ?
    /* close stdin and stdout */
    io_close(STDIN_FILENO);
    io_close(STDOUT_FILENO);

   
    //Les on redirige les entrées/sorties standard vers le socketpair child_srv
    //qui est lui même lié a srv_child du processus père
    /* duplicate input and output to stdin and stdout */
    if (dup2(child_srv, STDIN_FILENO) != STDIN_FILENO ||
        dup2(child_srv, STDOUT_FILENO) != STDOUT_FILENO)
    {
        io_close(child_srv);
        debug_msg(ERROR, "dup2 failed: %s", strerror(errno));
        _exit(EXIT_FAILURE);
    }

    //on execute la commande
    execve(argv[0], argv, envp);

        //gestion de la sortie de execve
    //je suppose que le fait que errno sorte d'on ne sais ou vient du cgi ...
    /* execve doesn't return on success */
    if (errno == ENOEXEC || errno == EACCES)
        debug_msg(ERROR, "CGI file not executable");
    else
        debug_msg(ERROR, "execve failed: %s", strerror(errno));

    /* prevent the call to the function registered with atexit */
    _exit(EXIT_FAILURE);
}



samedi 26 août 2006 à 13:12:27 | Re : Executer une commande

Bel0

log_close(); -> En effet, rien à voir dans le cas qui nous occupe :P //on ferme les entrées/sorties standards car on les redirige apres ? /* close stdin and stdout */ io_close(STDIN_FILENO); io_close(STDOUT_FILENO); -> On doit dabord fermer les file descriptors (fd) car sinon dup2 échoue quand on lui demande de faire la duplication car les fd sont encore valides et utilisables. //on execute la commande execve(argv[0], argv, envp); -> Dans le cas où tout se passe bien, execve ne retourne jamais puisque cette fonction écrase la section code en mémoire de notre application avec le code de la nouvelle application. Toutefois, il se peut que cet appel échoue car, par exemple, l'exécutable est introuvable ou le droit actuel du programme ne permet pas de lire le fichier exécutable... Il faut donc faire une gestion des erreurs, d'où la suite du code. //gestion de la sortie de execve //je suppose que le fait que errno sorte d'on ne sais ou vient du cgi ... /* execve doesn't return on success */ if (errno == ENOEXEC || errno == EACCES) debug_msg(ERROR, "CGI file not executable"); else debug_msg(ERROR, "execve failed: %s", strerror(errno)); -> pas tout à fait. errno est un grande classique dans la programmation système sous linux. C'est une variable de la librairie C qui contient le code d'erreur du dernier échec lors d'un appel. Cette variable indique donc la dernière erreur qui a eu lieu. man 2 execve pour avoir toutes les possibilités pour execve. strerror permet de récupérer une description du code d'erreur dans une chaine de caractère. Attention à l'utilisation de errno. Il me semble que errno n'est pas thread-safe (il me semble qu'ils sont en train de résoudre le problème). Je te conseil donc de mettre tous tes appels de fonction utilisant errno dans une seule et meme thread (si tu en as plusieurs). De cette façon, tu éviteras d'avoir des codes d'erreurs bizarres.

samedi 26 août 2006 à 13:22:30 | Re : Executer une commande

ffomnislash

Je peut aussi gérer le retour de execve d'une manière classic, pourquoi faire compliqué ?
res = execve()

Il faudrait aussi que a la fin je recré les "vrai" file descriptor STDIN_FILENO et STDOUT_FILENO non ?

Bon sinon j'ai cherché les fonctions nécessaires et les includes, voici la liste, ca pourra aider d'autres personnes:
#include <sys/types.h>
#include <sys/socket.h>
socketpair()
close()
dup2()
execve()

A part pour io_close() qui devient close(), il n'y a pas de différence avec le precedent code.




samedi 26 août 2006 à 13:34:38 | Re : Executer une commande

Bel0

Deux choses concernant STDIN_FILENO et STDOUT_FILENO: 1° Il n'y a pas besoin de recréer les "vrais" puisque tu veux avoir tout l'output du programme et ensuite que celui-ci se terminera. 2° Il n'y a de toute façon pas moyen de le faire. Il faut bien comprendre que si l'exec réussit, ce n'est plus ton programme qui s'exécute mais l'autre que tu vien s de lancer. C'est pour ca qu'on fait d'abord un fork ... pour éviter que notre programme ne se fasse écraser. Etant donné que ce n'est plus ton programme qui s'exécute, tu ne pourras pas restaurer les deux fd. Pour le programme que tu lances, il n'y a aucune différence. Si dans ce programme, on fait un printf, le résultat de ce printf sera écrit dans le socket unix et non pas sur la console. C'est pour ça qu'on dit que la sortie standard est redirigée. Fait peut-être quelque essai avec fork/exec pour bien saisir leur comportement.

samedi 26 août 2006 à 13:46:45 | Re : Executer une commande

ffomnislash

ah ok, ca m'oblige donc a executer execve dans un processus fils. Je prevoyais de le faire dans le processus principal etant donné que je ne ferait rien en parallèle. Je vais donc le lancer dans un processus fils et faire attendre le processus père.



1 2 3

Cette discussion est classé dans : solution, commande, resultat, executer, std


Répondre à ce message

Sujets en rapport avec ce message

executer une commande systeme [ par anthraxx ] bonjour,je voulais savoir si quelqu'un connait un moyen d'éxecuter des commandes su systemes d'exploitation sous Windows, et d'en récupérer l'output.I Executer une commande DOS en C++ [ par Kheo ] Quel API faut il utiliser pour executer une commande DOS depuis un source C++.J'ai trouvé _exec mais ca ne fait rien.../ Kheo / recuperer le resultat de rcmd [ par osta ] Bonjour, je suis debutant sur C et j'aimerais executer une commande Unix se trouvant dans une autre machine et recuperer le resultat dans une variable Comment executer une commande systeme sur Linux avec gcc ??? [ par jean84 ] Salut a tous ! Je voudrais savoir s'il existe une commande telle que system() sous linux ??? Si oui laquelle ??? Merci "Avant même de fonctionner, tou executer une commande dos [ par Daimadoshi ] slt tlm, voila je voudrai pendant un programme executer une commande dos. j'ai pensé lancer ma commande avec une ligne dans ce genre mais cela ne semb executer commande [ par edpunisher ] kikoo aidez moi pleaaaaaaaze je voudrai executer une commande unix et retourner le resultat dans une variable prédefini j offre 100 000$ a ki pourra m wxwidget + mysql [ par kyript ] bonjour a tous voila j'ai un gros soucis je cherche a mettre le resultat de certaine commande sql dans un wxString voici un examples de commande: M faire executer des lignes de commande a cmd [ par Mick7 ] Salut, voila je m'exlique je veux executer cmd (avec CreateProcess par exemple) et lui passer plusieurs lignes de commande une apres les autres  par e executer un commande du type cmd1|cmd2|...|cmdN [ par izou ] Salut j'ecrit un prog qui "simule" le shell ce n'est pas de la vrai simulation ,car le programme lit la commande puis demande au shell de l'executer; executer une commande [ par alphaone ] Comment executer une commande dans un programme et attendre la fin de l'excution pour continuer?


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,328 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.