IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Administration système Discussion :

Comment recevoir en directe le résultat d'une commande systeme en C++/C?


Sujet :

Administration système

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut Comment recevoir en directe le résultat d'une commande systeme en C++/C?
    Bnojour, je suis nouveau. J'ai une question. Je veux faire une application en C++ qui émet des commandes system et intéragit avec le retour, une sorte de terminal plus pret de hotwire que de xterm. Idéalement, j'aimerais que mon programme émet et recois lui même les output. À date, cela ma causé beaucoup de mots de tête. J'ai étudier 2 première possibilitées:

    Utiliser fopen pour recevoir le résultat:
    +facile
    +fonctionne sans trop se préocuper du système
    -ne commence à utiliser les output que quand la commande est terminé
    -demmande un buffer de plusieurs millions de char pour ne pas risqué de -faire un overflow

    Utiliser la redirection ">>" directement sur la ligne de commande dans un fichier temporaire:
    +il peut analyser en temps presquer réel les outputs dans une boucle infini
    -C'est une boucle infini, elle n'est pas capable de s'arrêter saud si j'ajoute && echo killme ou quelque chose du genre à la fin de la commande (très peu professionel)
    -C'est vraiment très insécure, trop insécure.


    Maintenant, après avoir lut quelque centaine de pages sur le fonctionement de bas niveau des systèmes POSIX, je commence à voir aparaitre la bonne solution:
    -forker mon prosessus
    -créer un pipe entre les 2 prosessus
    -executer la commande et redirectionné la sortie dans le pipe
    -attendre (ou provoquer) sa mort depuis le prosessus père en affichant/parsant les résultats à mesure.

    Le problème, c'est que à par forker le prosessus, je ne sais pas comment faire cela, les exemples sont très rare. Comment est-ce que je redirige la sortie de la commande dans le pipe et comment je le recois dans le prosessus père en directe?

    Merci de m'aider!

  2. #2
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Serait-tu québécois ? J'ai l'impression de lire ton accent.

    Je ne sais pas si le C++ propose des mécanismes de plus haut niveau, mais en C, ta dernière solution est la bonne.

    Ton programme doit ressembler à quelque chose du genre
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    /* Plein d'includes */
     
     
    /* Cette fonction finit par un exit() pour ne pas retourner dans le main() après */
    void exec_cmd(int *to_me, int *from_me) {
     
        /* Les dup donnent juste un "nouveau numéro" d'accès aux fichiers, cf man dup */
        dup2(to_me[0], STDIN_FILENO); /* Err */
        dup2(from_me[1], STDOUT_FILENO); /* Err */
     
        /* Fermer les descripteurs de fichier inutiles. Très important ! */
        /* Note : On peut fermer les 4 parce qu'on vient de dupliquer ceux dont on avait besoin */
        close(to_me[0]); /* Err */
        close(to_me[1]); /* Err */
        close(from_me[0]); /* Err */
        close(from_me[1]); /* Err */
     
        /* On exécute enfin la commande */
        execlp(/* blah */);
     
        /* Err */
        exit(EXIT_FAILLURE);
    }
     
     
    int main (void) {
        pid_t pid;
        int to_cmd[2], from_cmd[2];
     
        pipe(to_cmd); /* Err */
        pipe(from_cmd); /* Err */
     
        pid = fork(); /* Err */
        if (pid == 0)
            exec_cmd(to_cmd, from_cmd);
     
        /* Important ! Fermer les descripteurs de fichiers inutiles. */
        close(to_cmd[0]); /* Err */
        close(from_cmd[1]); /* Err */
     
     
        /* Envoyer les données en écrivant sur to_cmd[0]
         * Ces données seront reçues sur l'entrée standard du programme */
        write(/* blah */); /* Err */
     
        /* Lire le résultat sur from_cmd[1]
         * Ces données sont celles que la commande a envoyé sur la sortie standard */
        read(/* blah */); /* Err */
     
        /* On ferme les derniers descripteurs de fichier */
        close(to_cmd[0]); /* Err */
        close(from_cmd[1]); /* Err */
     
        /* On attend que le fils ait fini */
        wait(NULL);
     
        return EXIT_SUCCESS;
    }
    Les commentaires /* Err */ indiquent que tu dois tester la valeur de retour pour vérifier si il y a une erreur. En cas d'erreur, la fonction perror ou strerror devrait être appelée pour afficher un message d'erreur.

    Le programme globalement est assez simple, on crée deux pipes, un pour envoyer les données vers la commande, un pour lire les données renvoyées par la commande.
    Puis on fork. Lors d'un fork les descripteurs de fichier sont copiés, donc on se retrouve avec 4 descripteurs de fichier (2 par pipe) dans chaque processus.
    Le processus père, ferme les descripteurs de fichiers inutiles, envoie des données puis lit le résultat de la commande. Enfin il attend que le fils ait terminé avant de se terminer lui-même.

    Le fils, lui, avant d'exécuter la commande, commence par "brancher" les pipes sur ses entrée/sortie standard.
    En fait, pour chaque fichier ou pipe ouvert, le noyau crée une structure et y stocke tout plein d'information (entre autre la position de la "tête de lecture", celle qu'on modifie avec lseek/fseek), les descripteurs de fichiers (qui ne sont que des entiers) ne servent qu'à désigner une de ces structures.
    Les fonctions dup et dup2 ne servent qu'à donner un autre numéro à la structure qui est maintenant référencé par deux descripteurs (numéros) différents.
    Avec dup2 on choisit "numéro" de destination, et c'est cool, parce que comme ça, on peut remplacer l'entrée et la sortie standard par le descripteur de fichier qu'on veut. Comme ça, quand on va lancer la commande, elle pourra lire et écrire ses données comme elle le fait d'habitude sur ses entrées/sorties standard qui seront en fait branchés sur nos pipes.
    Ensuite, le fils ferme les descripteurs de fichiers inutiles. Les descritpeurs utiles ont été dupliqués, donc pour ceux-là, ça ne fait que décrémenter le "compteur d'ouverture" de la structure.
    Enfin exécuter la commande. Regarde le man, il existe plusieurs variantes de la commande execlp. Et puis on a bien de la chance parce que les descripteurs de fichiers ouverts sont conservés lors d'un exec.
    Les commandes exec se terminent toujours par une erreur. En effet, tu es censé remplacer ton processus actuel par un autre, donc si la suite de ton code s'exécute, c'est que l'exec a échoué.


    Ce genre de code est plein de subtilités, notamment le fait qu'il ne faut pas oublier de fermer tous les descripteurs de fichier inutiles.
    Et cela pour une raison simple.
    La fonction read est bloquante tant qu'il y a encore des données qui peuvent arriver. Si tous les descripteurs de fichiers en écriture sur ce pipe sont fermés, la fonction read renvoie 0 sans attendre.
    Donc oublier de fermer un descripteur de fichier peut conduire un read à être bloquant alors qu'il n'y a normalement plus aucune donnée qui puisse arriver.
    De même pour write, si tous les descripteurs en lecture sur ce pipe sont fermés, write doit échouer.
    Attention aussi au fait que write peut être bloquant si le buffer interne du pipe est plein.
    Donc le code que j'ai donné ci-dessus est potentiellement FAUX. En effet, si il y a beaucoup de données à écrire, la commande va en lire une partie, traiter puis renvoyer le résultat sur sa sortie standard.
    Mais toi, tu n'as pas fini d'écrire tes données, donc tu ne vas pas pouvoir lire le résultat. La commande va finir par se bloquer sur le write, et toi tu sera aussi bloqué sur ton write.
    Pouf, deadlock, y'a plus qu'à killer.

    C'est pour ça qu'en général on fait plutôt les read et les write dans deux processus ou threads séparés. Ou bien qu'on les gère avec un select. Mais pour ça je te laisse faire.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    Oui, tu as bien lut mon accent

    Merci de m'aider, je ne suis vraiment pas habitué à faire de la programation d'aussi bas niveau. J'ai essayé dede complèter ton code mais je n'ais pas réussi (oui, je suis vraiment un noob en prog système)

    Le code que j'utilise actuelement ressemble à :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #include  <unistd.h>
    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <errno.h>
    #include  <sys/wait.h>
    #include  <sys/types.h>
     
    void exec_cmd(int *to_me, int *from_me)
    {
     
        // Les dup donnent juste un "nouveau numéro" d'accès aux fichiers, cf man dup
        dup2(to_me[0], STDIN_FILENO); // Err
        dup2(from_me[1], STDOUT_FILENO); // Err
     
        // Fermer les descripteurs de fichier inutiles. Très important !
        // Note : On peut fermer les 4 parce qu'on vient de dupliquer ceux dont on avait besoin
        close(to_me[0]); // Err
        close(to_me[1]); // Err
        close(from_me[0]); // Err
        close(from_me[1]); // Err
     
        // On exécute enfin la commande
        execlp("ls", "ls", "-l", "/", 0);
     
        // Err
        sleep(15) ;
        exit(0);
    }
     
    int main(void)
    {
     pid_t  pid,pidfils ;
     int  status ;
     int to_cmd[2], from_cmd[2];
     
     pipe(to_cmd); // Err
     pipe(from_cmd); // Err
     
     
     
     printf("Processus pere de PID %d\n ",getpid()) ;
     
     switch(pid = fork())
     {
         case (pid_t)-1 :
            perror("naufrage... ") ;
            exit(2) ;
         case (pid_t)0 :
            printf(" Debut du processus fils PID=%d \n ",getpid()) ;
            system("echo commande executee par fils > /tmp/testprogsys.txt");
            exec_cmd(to_cmd, from_cmd);
         default:
            char bufferW[10000];
            write(to_cmd[0], bufferW, sizeof bufferW); // Err
     
            // Lire le résultat sur from_cmd[1]
            //* Ces données sont celles que la commande a envoyé sur la sortie standard
            char bufferR[10000];
            read(from_cmd[1], bufferR, sizeof bufferR); // Err
            printf("%s", bufferR);
     
            close(to_cmd[0]); // Err
            close(from_cmd[1]); // Err
     
            pidfils = wait(NULL) ; /* attention adresse ! ! ! */
            printf(" Mort du processus fils PID=%d\n ",pidfils) ;
      }
    return 0;
    }

    L'erreur est probablement stupide, je n'ai jmais fait sa et jamais vraiment travaillé en C pour autre chose que des petite fonctions.

    Ce que je remarque est que le sleep() ne s'éxécute jamais allors que avec le code du liens suivant ( http://drocourt.iut-amiens.fr/cours/...rog_sys3.xhtml ) il fonctionne. ce que je remarque aussi c'est que avec execlp("dmesg, "dmesg", "", "", 0) il affiche la ligne d'usage (-h) du prosessus sur mon terminal. Que manque t'il pour que ça fonctionne?

  4. #4
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Quelques remarques à propos de ton code.
    En C, les commentaires // ne sont pas standard, mais ils le sont en C++. Donc après, à toi de voir si tu veux faire du pure C ou du C++ à la C.

    J'espère que dans le programme final, tu testera le retour de toutes ces fonction. Généralement -1 indique une erreur, cf le man.
    Tous les arguments de execlp sont censés être des char*, y compris le dernier, donc tu devrais mettre NULL. D'ailleurs je suppose que si tu compile avec -W il va te dire un truc du genre "Warning: passing parameter makes pointer from integer without a cast".
    Le sleep ne s'exécute pas parce que le execlp a fonctionné. Je rappel que les fonction exec* en cas de succès remplacent le code de ton processus en mémoire par celui de l'exécutable donné.
    Au lieu de mettre exit(0), c'est plus propre de mettre exit(EXIT_SUCCESS). Cette macro a toujours la bonne valeur suivant le système, elle est définie dans stdlib.h.

    Dans la fonction main.
    Généralement on évite de faire appel à la fonction system(), c'est assez lent, et peut rapidement causer des soucis de sécurité. Mais tu as peut-être fait ça juste pour les tests.
    Tu n'est pas obligé de mettre tout le code du père dans le default du switch, puisque de toutes façons la fonction exec_cmd ne retournera jamais.
    Tu n'as pas fermé to_cmd[0] ni from_cmd[1] avant de commencer à envoyer/recevoir des données.
    Désolé hier j'ai inversé les bouts du pipe, pour lire/écrire des données vers la commande. Bien entendu on lit sur from_cmd[0] et écrit sur to_cmd[1]. Comme indiqué dans le man pipe, à l'extrémité 0 du pipe on ne peut que lire, et à l'extrémité 1 on ne peut que écrire.
    Ensuite, tu envoie à la commande, le contenu de bufferW alors que celui-ci n'est pas initialisé. (Et en C, les déclarations de variables se font toujours au début d'un bloc {}, mais cf la remarque du début.)
    À propos du read dans bufferR (tu lis sur le mauvais bout du pipe, mais c'est de ma faute). Tu ne lis que au maximum 10000 caractères, il faudrait peut-être mettre une boucle autour, mais là peut-être que tu voulais simplifier.
    La fonction printf, pour un format "%s", attend une chaîne de caractère qui se termine par un octet nul '\0'. Or quand tu fais un read, tu n'as aucune garantie que la chaîne se termine par octet nul, et d'ailleurs dans 99% des cas ce n'est pas le cas. Donc le printf risque de t'afficher ce que tu as lu avec des trucs en plus à la fin, ou alors il ne t'affichera pas le tout si un \0 se trouve en plein milieu de tes données.
    Et dernière chose le return 0 / return EXIT_SUCCESS comme dit précédemment.


    Et enfin, remarque plus générale : relire ce que j'ai dit précédemment à propos du deadlock.
    Ici le fait de faire des write sur le pipe avant de faire des read risque de marcher parce que sous linux les pipes ont un buffer de 4096 ou 65536 octets selon les versions, mais il se pourrait que quelqu'un sur un petit système ait mis seulement 16 octets de buffer.

    Et puis si le dmesg ne marche pas c'est parce que tu lui donne deux paramètres qui sont des chaînes vides.
    La fonction execlp prend un nombre d'arguments variable, tu peux très bien faire execlp("dmesg", "dmesg", NULL); ou bien execlp("ls", "ls", "/", "/home", "/home/toto", "/home/toto/video", "/home/toto/audio", NULL);.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    En effet, juste désinversé les io à règler le fait que sa ne fonctionnait pas. Escuse moi l'état purement dépriment du code qui suit. Je vais mettre la gestion d'erreur de l'ordre dans ce fouilli pur (et plein de raccouris pour sauver du tmeps dans mes testes). Je n'ai pas vraiment corrigé ce dont tu m'a parlé dans ton dernier post, mais je vais le faire.

    Pour l'instant, j'essai d'atteindre l'étape "just work" pour cette petite partie de code (qui est en C++ en passant, c'est juste que pour les pipe, sa doit être écrit en C, POSIX est comme sa). Actuelement, j'aimerais avoir un peut plus d'information sur le buffer, comment est-ce que je fais pour envoiyé seulement la limite de charactère et ensuite attendre avant la suivante? Je me sert du pipe du prosessus père pour lui envoyer un signal? Si oui, lequel?

    Et j'ai essailer de complèter mon semblan de terminal, mais très étrangement, certaine commde ne fonctionnent pas et d'autre parfaitement. Par exemple "ls -l /" ne fonctionne pas mais "ls -l" oui tout comme "ls /". Même chose pour "echo salut". Je l'ai passée avec atention dans GNU DDD, mais tout semble corecte! (en passant, oui je sais que j'ai une fuite de mémoire). Voici le code en question:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    #include <iostream>
    #include  <unistd.h>
    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <errno.h>
    #include  <sys/wait.h>
    #include  <sys/types.h>
     
    using namespace std;
     
    void exec_cmd(int *to_me, int *from_me, char* paramArray[])
    {
     
        dup2(to_me[0], STDIN_FILENO); // Err
        dup2(from_me[1], STDOUT_FILENO); // Err
     
        close(to_me[0]); // Err
        close(to_me[1]); // Err
        close(from_me[0]); // Err
        close(from_me[1]); // Err
     
        execvp(paramArray[0], paramArray);
     
        sleep(15) ;
        exit(0);
    }
     
    int main()
    {
        while (1)
        {
            cout << ">";
           string command;
            string tmp;
            int counter;
     
     
                counter = 0;
     
     
                getline(cin, command);
     
                tmp =command;
                while (tmp.find(" ") != -1)
                {
                    counter++;
                    tmp = tmp.substr((tmp.find(" ") +1), (tmp.length() - tmp.find(" ") - 1));
                    //cout << tmp << endl;
                }
                counter++;
     
                char** paramArray = new char*[counter+1];
                int i =0;
     
                while (command.find(" ") != -1)
                {
                    int paramLenght = command.find(" ");
                    char* param = new char[paramLenght+1];
                    strcpy(param, command.substr(0, paramLenght).c_str());
                    paramArray[i] = param;
                    command = command.substr((paramLenght +1), (command.length() - paramLenght - 1));
                    i++;
                }
                int paramLenght = command.length();
                char* param = new char[paramLenght+1];
                strcpy(param, command.c_str());
                paramArray[i] = param;
     
                pid_t  pid,pidchild ;
                 int  status ;
                 int to_cmd[2], from_cmd[2];
                 int find0A =0;
                 int findStart;
     
                 pipe(to_cmd); // Err
                 pipe(from_cmd); // Err
     
     
     
                 printf("I am parent PID %d\n ",getpid()) ;
     
                 switch(pid = fork())
                 {
                     case (pid_t)-1 :
                        perror("fatal error... ") ;
                        exit(2) ;
                     case (pid_t)0 :
     
                        printf(" I am child PID=%d \n ",getpid()) ;
                        system("echo commande executed by child > /tmp/test/testprogsys.txt");
                        exec_cmd(to_cmd, from_cmd, paramArray);
                     default:
                        char bufferW[10000];
                        close(from_cmd[1]);
                        close(to_cmd[0]);
                        write(to_cmd[1], bufferW, sizeof bufferW); // Err
     
                        // Lire le résultat sur from_cmd[1]
                        //* Ces données sont celles que la commande a envoyé sur la sortie standard
                        char bufferR[10000];
                        read(from_cmd[0], bufferR, sizeof bufferR); // Err
     
                        string output = bufferR;
                        int j= 0;
                        while (j < output.length())
                        {
                            findStart = j;
                            while ((bufferR[j] != 0x0A) && (j != output.length())) j++;
                            cout << "    >>" << output.substr(findStart, (j-findStart)) << endl;
                            j++;
                        }
                        //printf("%s", bufferR);
     
                        close(to_cmd[0]); // Err
                        close(from_cmd[1]); // Err
     
                        pidchild = wait(NULL) ; /* attention adresse ! ! ! */
                        printf(" Death of child PID=%d\n ",pidchild) ;
     
                }
              }
     
     
        return 0;
    }

    En passant, je voudrait ajouté que je suis encore loin de l'université, je fait actuelement un cours pré universitaire en programation (qui équivaut probablement à une formation professionel en france, mais je ne connais pas vraiment votre système scolaire). Donc ce genre de code me dépasse un peut, désoler d'être aussi perdu, je ne suis même pas suposé être capable de faire sa à mon niveau. Donc merci de ton indulgence

    EDIT: À oui, et j'oubliais, cette solution ne semble pas renvoyer les code serial pour les couleurs, est-ce possible de les récupérer eux aussi?

  6. #6
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Ce n'est pas vraiment un terminal que tu cherches à faire, mais un shell.
    Mais bon, en vrai, les shells ne récupèrent pas la sortie des programme pour l'afficher dans le terminal, ce sont les programmes qui affichent directement dans le terminal.

    Pour arriver à ce que ça marche, les messages d'erreur sont d'une grande utilité tu peux me croire. Donc tu devrais commencer par ça. Par exemple quand la lecture/écriture des pipes était inversé, tu aurait eut un message d'erreur un peu explicite.


    Il semblerait que ce soit ta boucle qui foire, en effet la première commande marche toujours, pas la deuxième.
    Les fonctions read et write renvoient le nombre d'octet lu/écrits dans le buffer et -1 en cas d'erreur, cf le man. D'ailleurs je te conseille très fortement de lire le man de chacun des appels système utilisé dans ton code, tu y apprendra des tas de choses.
    Pour la gestion de ton bufferW, je pense que (vu ton code) tu veux stocker le résultat complet en mémoire avant de le parcourir.
    Une méthode en C consiste à avoir un buffer qu'on alloue avec malloc, plus un petit buffer. Le read se fait dans le petit buffer, et on rajoute ce morceau de données lues à la fin du grand buffer avec memcpy. Ce grand buffer on l'agrandit si besoin avec realloc. Bien entendu ça sous-endend de connaître à tout moment la taille du buffer, et combien de données il contient.
    Et on continue tant que read n'a pas renvoyé 0. Parce que je le rappel, read renvoie 0 en fin de fichier (si c'est un fichier normal), ou si l'autre bout du pipe a été fermé (donc plus aucune donnée ne peut arriver).
    Il est possible de se passer du petit buffer intermédiaire, mais ça nécessite de jouer un peu plus avec les pointeurs.

    Mais c'est quand même un gros gâchis de stocker l'intégralité des données en mémoire alors qu'on peut les traiter au fur et à mesure qu'elles arrivent.
    Dans ton cas tu n'as qu'à lire un bloc de données, et le réafficher tel quel caractère par caractère, sauf si c'est un "\n", auquel cas tu affiche en plus un ">>".

    Mais bon, je sais qu'en C++ on fait pas trop de malloc, et il existe peut-être des mécanismes de plus haut niveau pour gérer ça. Mais comme déjà dit, je ne connais pas le C++.

    Sinon, je ne connais pas trop le C++ donc j'aurais du mal à te dire ce qui ne va pas dans ton programme.

    Pour une gestion simple et claire des erreurs je te propose la fonction suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void system_error(const char *msg)
    {
    	perror(msg);
    	exit(EXIT_FAILURE);
    }
    Et tu t'en sert comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    		err = pipe(to_cmd);	// Err
    		if (err == -1)
    			system_error("pipe");

    Et pour finir, remarque générale, un code propre c'est 50% de bugs en moins. Tu devrais essayer.


    À propos des couleurs, la commande ls se comporte différemment si sa sortie standard est connectée directement sur un terminal ou non. Il y a entre autre les couleurs qui ne sont plus envoyées. D'autres commandes se comportent ainsi. Tu peux la forcer à envoyer toujours les codes de couleur avec --color=always. Cf le man ls.
    Ce comportement permet d'avoir directement des données utilisables par d'autres commandes, comme grep.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    Encore une fois merci de me répondre. Pour ce que est du C++, c'est un language orienté objet, l'allocation de mémoire n'est pas statique comme dans le C, mais dynamique. Tu garde un pointeur sur tes donnée et dans le blocks de donnée que tu crée dynamiquement, tu garde un pointeur sur la donnée précédente. Comme sa le programme grossi et rapetisse par lui même si il est bien fait, pas besoin de passer par des fonction statique comme en C pour faire grossir un tableau ou quelque chose du genre, c'est beaucoup plus efficace au niveau programation, même si en fin du compte, au niveau assembleur, c'est très légèrement inférieur (parfois).

    Pour revenir à mon programme, non, je ne veut pas stocké tout le contenu de la commande, ce que je voudrait c'est que sa soit en temps réel. Je vais préciser ce que je veux faire, sa va aider à mieu comprendre mes besoins. Je fait un programme pour Linux et Unix en Qt/KDELIBS. Le programme va être comme une couche supérieur au terminal. Il va permettre de gérer des script avec cron et manuelement ainsi que fournir un terminal nouvelle génération à l'administrateur du système. Je vais commencer par faire une implémentation de bash et de terminal standart avant de me lancer dans quelque chose plus évoluer comme Microsoft PowerShell ou Red Hat HotWire. Mais le but est, dans le future, de faire quelque chose proche de hotwire mais avec des choix de conseption différent. si tu ne connais pas hotwire, voici ce que c'est en image http://picasaweb.google.com/cgwalter...20308695541106
    C'est probablement l'avenir de la ligne de commande, même si ce genre de concepte est très peu mature, c'est une avenue très intéressante pour les administrateur système. Donc mon projet est de faire un vrai centre d'administration système.

    La 1er étape d'une longue liste est de réussir à faire un terminal fonctionel qui est capable de faire intéragir les sorties avec l'application, pas juste les afficher, si ce n'étais que sa un system(bash); avec un petit fork dans un proxy aurais sufit. Donc ce que je veux faire est de recevoir les données en temps réel. Je ne peut pas prévoir ce que j'utilisateur va rentrer comem commande. Sa peut être un make de 30millions de charactère comme un ls de 10. Il faut que je puisse gérer les donnée au fur et à mesure qu'elle arrive, et c'est actuelement la ou je butte avec ce terminal.

    L'interface de mon programme est fait, et il marche plus ou moin avec un buffer dans un fichier, mais, même si c'est un défit pour moi, qui a des connaissances très limiter en C, j'aimerais vraiment faire ce truc de manière commode et standard.

    Surement que maintenan mon but est plus claire, ce qui, comme la claireté du code ne peut être que bénifique pour l'avancement de mon projet. Donc, comment je fait pour être sur de syncroniser les buffers pour être sur de ne manquer aucune donnée dans le prosessus mère tout en ayant le buffer le plus petit possible?

    EDIT:
    Voici le code avec la gestion des erreurs. Il y en as plusieurs et sa semble être ce qui prévient la seconde comamnde de fonctionner
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    #include <iostream>
    #include  <unistd.h>
    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <errno.h>
    #include  <sys/wait.h>
    #include  <sys/types.h>
     
    using namespace std;
     
    void exec_cmd(int *to_me, int *from_me, char* paramArray[])
    {
     
        if (dup2(to_me[0], STDIN_FILENO) == -1)
          printf("Error dup2(to_me[0] child \n");
        if (dup2(from_me[1], STDOUT_FILENO) == -1)
          printf("Error dup2(from_me[1] child \n");
        if (close(to_me[0]) == -1)
          printf("Error close(to_me[0] child \n");
        if (close(to_me[1]) == -1)
          printf("Error close(to_me[1] child \n");
        if (close(from_me[0]) == -1)
          printf("Error close(from_me[0] child \n");
        if (close(from_me[1]) == -1)
          printf("Error close(from_me[1] child \n");
     
        if (execvp(paramArray[0], paramArray) == -1)
          printf("Error execvp(paramArray[0]  child \n");
     
        printf("Error exec_cmd, this message should not be displayed");
        sleep(15);
        exit(0);
    }
     
    int main()
    {
     
        string command;
        string tmp;
        int counter;
        int i =0;
        pid_t  pid,pidchild ;
        int  status ;
        int to_cmd[2], from_cmd[2];
        int findStart;
        int j= 0;
     
        if (pipe(to_cmd) == -1)
          printf("Error pipe to_cmd \n");
     
        if (pipe(from_cmd) == -1)
          printf("Error pipe from_cmd \n");
     
     
        while (1)
        {
            cout << ">";
     
     
     
                counter = 0;
     
     
                getline(cin, command);
     
                tmp =command;
                while (tmp.find(" ") != -1)
                {
                    counter++;
                    tmp = tmp.substr((tmp.find(" ") +1), (tmp.length() - tmp.find(" ") - 1));
                    //cout << tmp << endl;
                }
                counter++;
     
                char** paramArray = new char*[counter+1];
     
     
                while (command.find(" ") != -1)
                {
                    int paramLenght = command.find(" ");
                    char* param = new char[paramLenght+1];
                    strcpy(param, command.substr(0, paramLenght).c_str());
                    paramArray[i] = param;
                    command = command.substr((paramLenght +1), (command.length() - paramLenght - 1));
                    i++;
                }
     
                int paramLenght = command.length();
                char* param = new char[paramLenght+1];
                strcpy(param, command.c_str());
                paramArray[i] = param;
     
     
     
     
     
     
                 printf("I am parent PID %d\n ",getpid()) ;
     
                 switch(pid = fork())
                 {
                     case (pid_t)-1 :
                        perror("fatal error... ") ;
                        printf("Error pid_t \n");
                        exit(2) ;
                     case (pid_t)0 :
                        printf(" I am child PID=%d \n ",getpid()) ;
                        system("echo commande executed by child > /tmp/test/testprogsys.txt");
                        exec_cmd(to_cmd, from_cmd, paramArray);
                     default:
                        char bufferW[10000];
                        if (close(from_cmd[1]) == -1)
                          printf("Error close from_cmd[1] parent \n");
                        if (close(to_cmd[0]) == -1)
                          printf("Error close to_cmd[0] parent \n");
                        if (write(to_cmd[1], bufferW, sizeof bufferW) == -1)
                          printf("Error Write parent \n");
     
                        // Lire le résultat sur from_cmd[1]
                        //* Ces données sont celles que la commande a envoyé sur la sortie standard
                        char bufferR[10000];
                        if (read(from_cmd[0], bufferR, sizeof bufferR) == -1)
                          printf("Error read parent \n");
     
                        string output = bufferR;
                        while (j < output.length())
                        {
                            findStart = j;
                            while ((bufferR[j] != 0x0A) && (j != output.length())) j++;
                            cout << "    >>" << output.substr(findStart, (j-findStart)) << endl;
                            j++;
                        }
                        i =0;
                        j =0;
                        findStart =0;
                        output.clear();
                        //printf("%s", bufferR);
     
                        if (close(to_cmd[0]) == -1)
                          printf("Error close to_cmd[0] parent \n");
                        if (close(from_cmd[1]) == -1)
                          printf("Error close from_cmd[1] parent \n");
     
                        pidchild = wait(NULL) ; /* attention adresse ! ! ! */
                        printf(" Death of child PID=%d\n ",pidchild) ;
     
                }
              }
     
     
        return 0;
    }

  8. #8
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Encore une fois merci de me répondre. Pour ce que est du C++, c'est un language orienté objet, l'allocation de mémoire n'est pas statique comme dans le C, mais dynamique.
    En C l'allocation dynamique passe par des appels à la fonction malloc (et ses cousins realloc, memalign et autres).

    Comme sa le programme grossi et rapetisse par lui même si il est bien fait, pas besoin de passer par des fonction statique comme en C pour faire grossir un tableau ou quelque chose du genre, c'est beaucoup plus efficace au niveau programation, même si en fin du compte, au niveau assembleur, c'est très légèrement inférieur (parfois).
    Note bien que ce n'est pas le programme qui change de taille mais la place qu'il a alloué en mémoire lors de l'exécution.
    Les fonction static Ça n'a rien à voir, c'est des fonctions comme les autres, sauf que leur visibilité est limitée au fichier où elles sont écrites.
    Question efficacité, on va peut-être éviter d'en parler parce qu'il y a plusieurs points à prendre en compte : optimisabilité par le compilateur, et l'optimisabilité à la main. À chaque fois il faut tenir compte du temps de calcul de et la mémoire consommée. Bref, c'est pas trivial de comparer l'efficacité de deux langages.


    Je fait un programme pour Linux et Unix en Qt/KDELIBS. Le programme va être comme une couche supérieur au terminal. Il va permettre de gérer des script avec cron et manuelement ainsi que fournir un terminal nouvelle génération à l'administrateur du système. Je vais commencer par faire une implémentation de bash et de terminal standart avant de me lancer dans quelque chose plus évoluer comme Microsoft PowerShell ou Red Hat HotWire.
    Euh ouais... D'après ce que j'ai vu, HotWire c'est juste un shell graphique sans terminal. Il faudrait d'abord se mettre d'accord sur la terminologie.
    Un terminal c'était à l'origine un couple écran/clavier sans mémoire ni rien. En somme, un matériel complètement idiot. Ce matériel on le connectait à un ordinateur (qui n'était pas un pc et occupait plusieurs mètres cubes) pour pouvoir ouvrir une session sur le gros ordinateur, ce qui ensuite exécute un shell qui pourra interpréter les commandes.

    Maintenant, y'a plus de terminaux matériels, il y en a seulement des émulations logicielles. Quand tu ouvres par exemple xterm ou Konsole, le terminal, c'est juste la fenêtre capable d'afficher des caractères. Tout ce qui est affiché dedans, ce n'est plus le terminal qui le gère, c'est le programme qui est connecté à ce terminal (généralement un shell comme bash ou zsh).

    Basiquement, un shell, c'est un interpréteur de commande avec sa syntaxe et son langage.

    Donc ton projet c'est de faire soit un shell, soit une surcouche au shell, mais ce n'est pas un terminal. Le choix entre un shell et une surcouche au shell dépend de si tu veux interpréter les commandes toi-même ou non.



    Maintenant, au niveau du code :
    Pourquoi ne pas sortir le code du default du switch ? Évite de mettre beaucoup de code dans les switch c'est assez lourd à lire.
    Quand tu traite les erreurs, fait un appel à la fonction perror, ou bien utilise strerror pour avoir un message d'erreur plus précis. De plus pas besoin de tester si execvp renvoie -1, comme indiqué dans le man, les fonction exec* renvoient toujours -1.

    Comme dit dans un message précédent le write sur to_cmd[1] risque de provoquer un interblocage.

    Si tu veux que le buffer soit le plus petit possible, tu peux faire un truc genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    char c;
    ssize_t nr;
     
    do {
        nr = read(from_cmd[0], &c, sizeof(char));
        if (nr == -1)
            system_error("read");
        if (nr > 0) {
            // Traiter le caractère c
            printf("%c", c);
        }
    } while (nr > 0);
    Le seul problème c'est que l'utilisation d'un buffer d'un seul caractère est assez inefficace.
    Donc tu peux modifier le code ainsi pour utiliser un buffer plus grand
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    char buf[4096];
    ssize_t nr;
    int i;
     
    do {
        nr = read(from_cmd[0], buf, 4096*sizeof(char));
        if (nr == -1)
            system_error("read");
        if (nr > 0) {
            // Traiter le buffer buf
            for (i = 0; i < nr; i++) {
                // Traiter buf[i];
            }
        }
    } while (nr > 0);
    Tu remarquera bien que la boucle for ne fait que nr itérations, et pas 4096.

    Comment être sûr que ces boucles do-while vont bien se terminer correctement ? (Déjà dit, mais je crois que c'est pas superflus d'en remettre une couche.)
    La fonction read(), sur un pipe, est bloquante si il n'y a pas de données à lire mais qu'il y a toujours au moins un écrivain à l'autre bout qui n'a pas fait de close sur le pipe. Quand tous les écrivain sur le pipe ont fait un close, la fonction read renvoie 0.
    D'où l'importance de bien faire un close sur tous les descripteurs de fichiers inutiles.


    Par contre, pour le fait que le programme ne marche pas bien, déjà tu ne termine pas le tableau paramArray par le pointeur NULL.
    En effet, comme indiqué dans le man, le tableau d'arguments est un tableau de chaînes de caractère terminés par un caractère nul (i.e. '\0'), ce tableau est lui-même terminé par un pointeur NULL.
    Ces trucs-là, ça s'invente pas, c'est pour ça que c'est écrit dans le man.


    Pour le fait que tu ne puisse pas exécuter deux commandes à la suite, c'est peut-être parce qu'il faudrait créer les pipes dans la boucle principale, et pas avant.
    En effet, quand la commande tapée a fini de s'exécuter, les pipes ne sont plus utilisables, c'est bien pour ça que tu les ferme. Il va falloir créer d'autres pipes avant d'exécuter une autre commande.
    Tu ferai mieux à mon avis de les créer juste avant le fork. Histoire de ne pas attendre que l'utilisateur tape une commande avec les deux pipes en attente.


    Étant donné que la sortie des programmes peut être du texte ou des données binaires, tu ne devrait pas utiliser une string pour réafficher le la sortie de la commande, mais vraiment utiliser un tableau d'octets.


    Dernier truc pour lequel ton code ne marche pas, la variable i est initialisée à 0 lors de sa déclaration, mais tu t'en sert à l'intérieur de ta boucle principale, donc au deuxième tour, ça ne marche plus.

    Sinon, fait attention aussi à ce que te renvoie getline(), si j'appuie sur Ctrl+D ça envoie un signal de "fin de fichier", et donc ton programme part en boucle infinie.


    Voilà une version qui marche de ton programme.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    #include <iostream>
    #include  <unistd.h>
    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <errno.h>
    #include  <sys/wait.h>
    #include  <sys/types.h>
     
    using namespace std;
     
     
    void system_error(const char *msg)
    {
    	perror(msg);
    	exit(EXIT_FAILURE);
    }
     
     
    void exec_cmd(int *to_me, int *from_me, char *paramArray[])
    {
    	if (dup2(to_me[0], STDIN_FILENO) == -1)
    		system_error("dup2(to_me[0])");
     
    	if (dup2(from_me[1], STDOUT_FILENO) == -1)
    		system_error("dup2(from_me[1])");
     
    	if (close(to_me[0]) == -1)
    		system_error("close(to_me[0])");
     
    	if (close(to_me[1]) == -1)
    		system_error("close(to_me[1])");
     
    	if (close(from_me[0]) == -1)
    		system_error("close(from_me[0])");
     
    	if (close(from_me[1]) == -1)
    		system_error("close(from_me[1])");
     
     
    	execvp(paramArray[0], paramArray);
     
    	system_error("execvp(paramArray)");
    	exit(EXIT_FAILURE);
    }
     
    int main(void)
    {
     
    	string command;
    	string tmp;
    	int counter;
    	int i = 0;
    	pid_t pid, pidchild;
    	int status;
    	int to_cmd[2], from_cmd[2];
    	int findStart;
    	int j = 0;
     
    	while (1) {
    		cout << "> ";
     
     
    		counter = 0;
     
     
    		getline(cin, command);
     
    		tmp = command;
    		while (tmp.find(" ") != -1) {
    			counter++;
    			tmp = tmp.substr((tmp.find(" ") + 1),
    					 (tmp.length() - tmp.find(" ") - 1));
    			//cout << tmp << endl;
    		}
    		counter++;
     
    		char **paramArray = new char *[counter + 2];
     
     
    		i = 0;
    		while (command.find(" ") != -1) {
    			int paramLenght = command.find(" ");
    			char *param = new char[paramLenght + 1];
    			strcpy(param, command.substr(0, paramLenght).c_str());
    			paramArray[i] = param;
    			command =
    				command.substr((paramLenght + 1),
    					       (command.length() -
    						paramLenght - 1));
    			i++;
    		}
     
    		int paramLenght = command.length();
    		char *param = new char[paramLenght + 1];
    		strcpy(param, command.c_str());
    		paramArray[i] = param;
    		paramArray[i+1] = NULL;
     
     
     
     
    		if (pipe(to_cmd) == -1)
    			system_error("pipe(to_cmd)");
     
    		if (pipe(from_cmd) == -1)
    			system_error("pipe(from_cmd)");
     
     
     
     
     
    		printf("I am parent PID %d\n", getpid());
     
    		switch (pid = fork()) {
    		case (pid_t) -1:
    			system_error("fork");
    		case (pid_t) 0:
    			printf(" I am child PID=%d\n", getpid());
    			//system("echo commande executed by child > /tmp/test/testprogsys.txt");
    			exec_cmd(to_cmd, from_cmd, paramArray);
     
    		// Par défaut il n'y a rien à faire
    		// default:
    		}
     
     
     
    		char bufferW[10000];
    		if (close(from_cmd[1]) == -1)
    			system_error("close(from_cmd[1])");
     
    		if (close(to_cmd[0]) == -1)
    			printf("close(to_cmd[0])");
     
    		/*if (write(to_cmd[1], bufferW, sizeof bufferW) == -1)
    			printf("write[bufferW]");*/
     
     
     
     
    		// Lire le résultat sur from_cmd[1]
    		//* Ces données sont celles que la commande a envoyé sur la sortie standard
     
    		char bufferR[10000];
    		ssize_t nr;
    		int i;
     
    		printf("    >>");
     
    		do {
    			nr = read(from_cmd[0], bufferR, sizeof bufferR);
    			if (nr == -1)
    				system_error("read(bufferR)");
     
    			// Sortie de la boucle si il n'y a plus rien à lire
    			if (nr == 0)
    				break;
     
    			for (i = 0; i < nr; i++) {
     
    				// Traitement bateau de bufferR
    				if (bufferR[i] == '\n')
    					printf("\n    >>");
    				else
    					printf("%c", bufferR[i]);
    			}
    		} while (nr > 0);
    		printf("\n");
     
     
    		if (close(to_cmd[1]) == -1)
    			system_error("close(to_cmd[1])");
    		if (close(from_cmd[0]) == -1)
    			system_error("close(from_cmd[0])");
     
    		pidchild = wait(NULL);	/* attention adresse ! ! ! */
    		printf(" Death of child PID=%d\n", pidchild);
    	}
     
     
    	return 0;
    }
    C'est écrit selon ma façon de coder, mais en essayant de conserver au maximum ton code C++. Si t'as une question n'hésite pas.

    Perso je trouve ça archi crade les déclaration n'importe où dans le code, mais après tout tu fais ce que tu veux dans ton code.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    Magic! Merci mille fois!

    Je vais commenter le code que tu m'a fait et je vais le posté dans le 1er message quand je vais avoir le temps. Je peux dire que j'en aurais apris des truc dans les 5 dernier jours!

    Une petite dernière comme sa qui me passe par la tête. Quand ont fait tab ou espace 2 seconde avec une ligne vide sur bash, sa affiche la liste des commandes, y a t'il une commande en particulié pour faire cela? J'ai fait un petit code pour, mais je le trouve un peut lent.

  10. #10
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    C'est une fonction de complétion de la librairie readline. Peut-être que tu devrais utiliser cette librairie.

    Sinon, si toi, tu obtiens des performances moindre, c'est peut-être parce que dans la librairie readline, les complétions possibles sont mises en cache, et pas lues sur le disque à chaque fois.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  11. #11
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    Bon, le programme est presque fini, il ne me reste plus qu'a regler quelque details. Voici ceux qui concerne le code si-dessu:

    -Pour le stdin, comment je fait? J'imagine que les 2 autre pipes servent a ca, mais comment je fait pour savoir quand le stdin a besoin d'une entree? Dans l'etat actuel du logiciel, les stdin apparaissent dans le terminal avec lequel j'ai ouvert mon logiciel, ils ne sont pas rediriger dans mon application (ce qui la ferais planter de toute facon)

    -Et quand l'utilisateur appel une commande avec gui, comme gdb -tui, nano, emacs, vi, aptitude, iirc, lynx, etc. Comment je fait pour le savoir, au moin pour eviter de faire planter l'application ou au mieu, comment les gerer (je peut m'artranger avec Qt, mais pour donner la taille de l'affichage et tout ca et comemnt gerer ce qui va arriver (et comment il va arriver)).

    EDIT:
    -Quand je kill le pid du prosessus fils, comment eviter que la segfault de prosessus pere.

  12. #12
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Citation Envoyé par Elv13 Voir le message
    -Pour le stdin, comment je fait? J'imagine que les 2 autre pipes servent a ca, mais comment je fait pour savoir quand le stdin a besoin d'une entree? Dans l'etat actuel du logiciel, les stdin apparaissent dans le terminal avec lequel j'ai ouvert mon logiciel, ils ne sont pas rediriger dans mon application (ce qui la ferais planter de toute facon)
    Il faut bien faire la différence entre stdin, stdout et stderr, ça correspond à trois descripteurs de fichiers complètement différents. Un programme lit sur son entrée standard, et écrit généralement sur la sortie standard, et écrit éventuellement les erreurs sur la sortie d'erreur.
    Tu parles de l'entrée standard de quel programme ? Celui lancé par ton shell ? À priori tu n'as pas de moyen de savoir si celui-ci attend une entrée. Tu peux peut-être tester avec la fonction select et en indiquant l'option O_NONBLOCK sur le pipe (via la fonction fcntl).



    -Et quand l'utilisateur appel une commande avec gui, comme gdb -tui, nano, emacs, vi, aptitude, iirc, lynx, etc. Comment je fait pour le savoir, au moin pour eviter de faire planter l'application ou au mieu, comment les gerer (je peut m'artranger avec Qt, mais pour donner la taille de l'affichage et tout ca et comemnt gerer ce qui va arriver (et comment il va arriver)).
    Dans GUI, le G signifie "graphic". C'est pas parce que tu as une interface avec des trucs qui s'affichent n'importe où sur ton écran que ça devient une GUI. Ces programmes possèdent des interfaces en console, qui sont juste un peu plus évolué que la simple ligne de commande. En particulier, ils savent utiliser les fonctionnalités "avancées" des termiaux.

    EDIT:
    -Quand je kill le pid du prosessus fils, comment eviter que la segfault de prosessus pere.
    Ceci n'arrive pas si tu as codé correctement ton programme.
    Tu es sûr que c'est bien un SIGSEGV que ton processus reçoit ? (le signal envoyé en cas de segfault.) Et pas un SIGPIPE ? Qui lui est envoyé quand tu essaye d'écrire sur un pipe fermé à l'autre bout.
    man 7 pipe
    NB : ce n'est pas un pid qu'on kill mais un processus. Le PID n'est qu'un IDentificateur de Processus.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  13. #13
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    J'imagine que je parle de l'entree standart. Quand je fait read, sudo et compagnie. Comment je fait exactement pour gerer cela?

    Pour les UI, comment je fais. Quel son ces fonctions "avancee" et ou se trouve leur documentation (vt100?)

    Et pour le kill

    Kling (kling) crashed and caused the signal 11 (SIGSEGV).
    This backtrace appears to be of no use.
    This is probably because your packages are built in a way which prevents creation of proper backtraces, or the stack frame was seriously corrupted in the crash.

    [?1034hUsing host libthread_db library "/lib/libthread_db.so.1".


    il se peut par contre que sa soit a un niveau plus haut que le shell. sa reste que sa le fait a cause du kill.

  14. #14
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Les programmes su et sudo sont un peu particulier, ils ne lisent pas vraiment l'entrée standard (ils lisent le terminal directement pour s'assurer qu'il n'y a pas un programme relais qui intercepte le mot de passe). Ces programmes ne marcheront donc pas dans ton shell.

    La commande read, est elle aussi assez particulière car c'est une commande intégrée au shell, il n'y a pas de programme qui s'appel "read".

    Un programme simple qui permet de tester les entrées sorties standard, c'est "cat". Appelé sans argument il se contente de lire son entrée standard et renvoyer les données sur sa sortie standard.


    Maintenant, je ne sais pas ce que tu veux faire avec ces programmes que tu lance. Si tu veux afficher une fenêtre chaque fois que le programme tente de lire sur son entrée standard, ça risque de pas être simple puisqu'un programme peut très bien attendre des données sur son entrée standard pour un temps donné et continuer son exécution en cas d'inactivité.



    Pour le programme qui plante, si tu as compilé avec les symboles de débuggage, tu as certainement un gros dépassement de tampon.
    Si ça n'arrive qu'avec les programmes qui ont une interface en évoluée en console, je pense que ça concerne la lecture de la sortie standard du programme. En effet la sortie de ces programmes est souvent assez grosse car elle contient plein de caractères de contrôle.
    Je ne sais pas ce que tu compte faire pour ces programmes. Dans tous les cas, c'est quasiment mission impossible de créer une vrai interface graphique à partir de la sortie de ces programmes. Au mieux tu pourrai peut-être ouvrir un vrai terminal et faire le relais entre les entrées/sorties du programme et le terminal. (Rappel encore une fois : terminal == boite noir qui affiche du texte, rien de plus.)
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  15. #15
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    le programme tente de lire sur son entrée standard, ça risque de pas être simple puisqu'un programme peut très bien attendre des données sur son entrée standard pour un temps donné et continuer son exécution en cas d'inactivité.
    Je sais que sa peut continuer et j'ai programmer en consequence. Mon application possede des thread et je garde des pointeurs sur l'object Shell (et les pipe) en tout temps meme si il ne fait pas partie du meme thread donc le programme peut continuer en parallele du GUI. Pour ce qui est des inputs, c'est la ligne de commande qui (va) recevoir un signal emit par le thread et qui va requisitionner le focus et indiquer clairement pourquoi.

    La commande read, est elle aussi assez particulière car c'est une commande intégrée au shell, il n'y a pas de programme qui s'appel "read".
    Parfait, c'est plus simple comme cela. Je savais pour cd, mais je n'avait pas verifier si read etais une de ces commande. Je vais la reimplanter comme j'ai fait pour cd. C'est avec envz.h?

    Pour les segfault causees par un kill, tu a l'entierter de la backtrace dans la post plus haut. Quand j'ai reeimplemente le ctrl+c dans mon terminal, je ferme les pipe et detruit l'objet avant de faire le kill, ce qui fonctionne, mais quand le kill est externe, ca cause la segfault sans backtrace. Les commande a interface ne provoque pas de plantage, elle ne fond rien et je n'ai qua les killer depuis mon application pour pouvoir continuer.

    Pour reimplementer les commande a interface, je comptais effectivement prendre une konsole kpart de KDE, il me faut quand meme la reponse a ma question, comment je fait pour savoir que c'est une commande a interface? Elle n'envoi rien sur la sortie standart. Sa reste que si tu le sais (ou sais ou trouver la reponse), j'aimerais quand meme savoir comment ca fonctionne.

    Et pour savoir ce que je veux faire avec les commande, je ne compte pas toute les reimplemanter, juste celle pour lesquelle l'utilisabilitee en serais amiliorer. Par exemple, pour un nouveau, chmod c'est quand meme complexe, la majoriter ne ferons de 777, 755 ou celle qu'ils conaitrerons par coeurs, quelque petite case a cocher ne sont pas de trop, tant quelle n'empeche pas un utilisateur plus avance de pouvoir continuer a taper sa commande en les ignorant.

    Je comprend bien la philosophie et les ligne d'utilisabilitee des terminaux en mode texte et des interfaces graphique. C'est paradoxale, un privilegie la simpliciter l'autre, l'efficacitee. Mais passer du mode graphique au mode texte pour un debutant est complexe. Meme pour un utilisateur avancee, il est impossible de savoir toute les options de toute les commandes. C'est ce que mon application vise comme auditoire. Les administrateur systeme et les webmestres (webmasters), des personne qui ne passent pas tout leur temps dans un terminal et dont les conaissances sont limitees.

    J'ai ecrit un parser qui indexe les comamnde, un qui indexe leur options (sans aucune execution, pas de popen avec la commande + -h, c'est clean et sa marche, je pense que je suis le premier a reussir ce coup la , 13k options ont ete repertorier sur mon systeme, soit probablement 30% ou 40% du total, je pense que c'est acceptable), un parseur similaire pour trouver des exemple d'utilisation de ces commandes, un parseur qui interagit avec crontab pour produire des string lisible des cron job (formatage des dates), je peut ausser ajouter des cron job depuis mon application avec un GUI simple, un parseur de manpage (oui, un autre, mais j'avais des besoin different des autres). J'ai aussi fait un debuggeur de bash avec breakpoint et tout (il est incomplet, il ne lit pas les if, while, for et until encore, et les variables sont mal gerees). Il y a aussi un gestionnaire de scripte (avec les meme lacunes que le debuggeur pour l'instant) et un visioneur de progression de l'execution des scriptes. Tout est gerer dans une base de donnee (exepter le contenu des manpage pour des raisons de taille et l'index des commande, pour qu'il se mettes a jour a chaque execution). La base de donner me permet aussi de faire des logs assez complet et plus simple a lire qu'un .bash_hystory.

    En gros, je pense que sa s'enviens bien!

  16. #16
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Citation Envoyé par Elv13 Voir le message
    Je sais que sa peut continuer et j'ai programmer en consequence. Mon application possede des thread et je garde des pointeurs sur l'object Shell (et les pipe) en tout temps meme si il ne fait pas partie du meme thread donc le programme peut continuer en parallele du GUI. Pour ce qui est des inputs, c'est la ligne de commande qui (va) recevoir un signal emit par le thread et qui va requisitionner le focus et indiquer clairement pourquoi.
    Une ligne de commande c'est un ensemble de caractères qui contient le nom de la commande, et les options. Une ligne de commande n'envoie rien et ne reçoit rien, ce n'est qu'une chaîne de caractère.
    Tu serai prié d'employer le vocabulaire approprié de manière à avoir des échanges plus constructifs.
    Le problème que je soulevais c'est que si le programme demande une entrée utilisateur, tu ouvre une fenêtre. Si au bout de 10 secondes le programme a décidé que c'était trop tard, tu n'as que peu (voir pas) de moyen de détecter cela pour fermer la fenêtre.


    Citation Envoyé par Elv13 Voir le message
    Parfait, c'est plus simple comme cela. Je savais pour cd, mais je n'avait pas verifier si read etais une de ces commande. Je vais la reimplanter comme j'ai fait pour cd. C'est avec envz.h?
    envz ? Qu'on à voir les variables d'environnement là dedans ?
    Dans un shell, il y a deux types de variables, les variables normales et les variables d'environnement. Seul les variables d'environnement sont transmises aux processus fils.


    Citation Envoyé par Elv13 Voir le message
    Pour les segfault causees par un kill, tu a l'entierter de la backtrace dans la post plus haut.
    Quedall !
    Citation Envoyé par Elv13 Voir le message
    Quand j'ai reeimplemente le ctrl+c dans mon terminal, je ferme les pipe et detruit l'objet avant de faire le kill, ce qui fonctionne, mais quand le kill est externe, ca cause la segfault sans backtrace. Les commande a interface ne provoque pas de plantage, elle ne fond rien et je n'ai qua les killer depuis mon application pour pouvoir continuer.
    À priori un shell classique se contente d'envoyer un SIGINT à tous les processus impliqués dans le pipe.
    J'ai pas bien compris l'histoire de backtrace/pas backtrace. Si tu veux une backtrace tu exécute ton programme dans un débuggeur et une fois killé, tu fais un bt (sous gdb).


    Citation Envoyé par Elv13 Voir le message
    Pour reimplementer les commande a interface, je comptais effectivement prendre une konsole kpart de KDE, il me faut quand meme la reponse a ma question, comment je fait pour savoir que c'est une commande a interface? Elle n'envoi rien sur la sortie standart. Sa reste que si tu le sais (ou sais ou trouver la reponse), j'aimerais quand meme savoir comment ca fonctionne.
    Un programme à interface en console envoie tout sur la sortie standard ou sur la sortie d'erreur. C'est son seul moyen d'écrire sur le terminal.
    Tu n'a aucun moyen sûr de savoir si un programme possède une telle interface. Tout ce que tu peux faire c'est essayer d'intercepter les certains codes de contrôle de du terminal (qui commencent par "\e[" avec \e qui représente le caractère de code 0x1b).

    Citation Envoyé par Elv13 Voir le message
    Et pour savoir ce que je veux faire avec les commande, je ne compte pas toute les reimplemanter, juste celle pour lesquelle l'utilisabilitee en serais amiliorer. Par exemple, pour un nouveau, chmod c'est quand meme complexe, la majoriter ne ferons de 777, 755 ou celle qu'ils conaitrerons par coeurs, quelque petite case a cocher ne sont pas de trop, tant quelle n'empeche pas un utilisateur plus avance de pouvoir continuer a taper sa commande en les ignorant.
    Ma question portait plutôt sur le fait que tu construise une interface "à la volée" ou pas.

    Citation Envoyé par Elv13 Voir le message
    Mais passer du mode graphique au mode texte pour un debutant est complexe. Meme pour un utilisateur avancee, il est impossible de savoir toute les options de toute les commandes.
    C'est à ça que servent les man.


    Citation Envoyé par Elv13 Voir le message
    J'ai ecrit un parser qui indexe les comamnde, un qui indexe leur options (sans aucune execution, pas de popen avec la commande + -h, c'est clean et sa marche, je pense que je suis le premier a reussir ce coup la , 13k options ont ete repertorier sur mon systeme, soit probablement 30% ou 40% du total, je pense que c'est acceptable)
    Tu aura bien du mal à maintenir ton index à jour au fil des mises à jour des commandes, c'est pour cela que cette solution n'est jamais choisie. Cela dit, non, tu n'es pas le premier à faire ça. D'une certaine manière l'autocomplétion de bash le fait. En effet, bash intègre des tas de scripts pour compléter seulement avec ce qui est possible.


    Citation Envoyé par Elv13 Voir le message
    un parseur de manpage (oui, un autre, mais j'avais des besoin different des autres).
    Renseigne-toi bien sur ce qui existe déjà avant de réinventer la roue carrée.

    Citation Envoyé par Elv13 Voir le message
    J'ai aussi fait un debuggeur de bash avec breakpoint et tout (il est incomplet, il ne lit pas les if, while, for et until encore, et les variables sont mal gerees).
    Il existe bashdb comme débuggeur de script shell bash.

    Citation Envoyé par Elv13 Voir le message
    La base de donner me permet aussi de faire des logs assez complet et plus simple a lire qu'un .bash_hystory.
    Le problème est toujours le même. Soit tu utilise un format facilement lisible par l'homme avec un simple éditeur de texte, mais plus difficile à lire par un programme, soit tu fais le contraire.
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

  17. #17
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2008
    Messages : 69
    Points : 62
    Points
    62
    Par défaut
    Une ligne de commande c'est un ensemble de caractères qui contient le nom de la commande, et les options. Une ligne de commande n'envoie rien et ne reçoit rien, ce n'est qu'une chaîne de caractère.
    Tu serai prié d'employer le vocabulaire approprié de manière à avoir des échanges plus constructifs.
    Le problème que je soulevais c'est que si le programme demande une entrée utilisateur, tu ouvre une fenêtre. Si au bout de 10 secondes le programme a décidé que c'était trop tard, tu n'as que peu (voir pas) de moyen de détecter cela pour fermer la fenêtre.
    La ligne ou on ecrit le commandes, c'est a quoi je fesais allusion.

    Quedall !
    Si tu y tiens:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    Program received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0x2afdcf5228c0 (LWP 27819)]
    QDBusConnectionPrivate::handleMessage (this=0x6db510, amsg=@0x7fffe43f04c0) at qdbusintegrator.cpp:488
    /home/kde-devel/kde/src/qt-copy/src/dbus/qdbusintegrator.cpp:488:17536:beg:0x2afdcceb779f
     
    #0  QDBusConnectionPrivate::handleMessage (this=0x6db510, amsg=@0x7fffe43f04c0) at qdbusintegrator.cpp:488
    #1  0x00002afdcceba64b in qDBusSignalFilter (connection=<value optimized out>, message=<value optimized out>, data=<value optimized out>) at qdbusintegrator.cpp:481
    #2  0x00002afdcfc6806f in dbus_connection_dispatch (connection=0x6df5d0) at dbus-connection.c:4267
    #3  0x00002afdcceaa989 in QDBusConnectionPrivate::doDispatch (this=0x6db510) at ./qdbus_symbols_p.h:100
    #4  0x00002afdcceaac16 in QDBusConnectionPrivate::socketRead (this=0x6db510, fd=14) at qdbusintegrator.cpp:1044
    #5  0x00002afdccee542a in QDBusConnectionPrivate::qt_metacall (this=0x6db510, _c=QMetaObject::InvokeMetaMethod, _id=4, _a=0x7fffe43f07b0) at .moc/release-shared/moc_qdbusconnection_p.cpp:88
    #6  0x00002afdc6c825cc in QMetaObject::activate (sender=0x6e1c20, from_signal_index=<value optimized out>, to_signal_index=4, argv=0x2afdcfc852d9) at kernel/qobject.cpp:3010
    #7  0x00002afdc6cbb3fe in QSocketNotifier::activated (this=0x6db510, _t1=14) at .moc/release-shared/moc_qsocketnotifier.cpp:81
    #8  0x00002afdc6c887ff in QSocketNotifier::event (this=0x6e1c20, e=0x7fffe43f0d70) at kernel/qsocketnotifier.cpp:326
    #9  0x00002afdc8905cee in QApplicationPrivate::notify_helper (this=0x66aa00, receiver=0x6e1c20, e=0x7fffe43f0d70) at kernel/qapplication.cpp:3772
    #10 0x00002afdc890a8ce in QApplication::notify (this=0x7fffe43f1060, receiver=0x6e1c20, e=0x7fffe43f0d70) at kernel/qapplication.cpp:3739
    #11 0x00002afdc9eeb210 in KApplication::notify (this=0x7fffe43f1060, receiver=0x6e1c20, event=0x7fffe43f0d70) at /home/kde-devel/kde/src/kdelibs/kdeui/kernel/kapplication.cpp:311
    #12 0x00002afdc6c6e588 in QCoreApplication::notifyInternal (this=0x7fffe43f1060, receiver=0x6e1c20, event=0x7fffe43f0d70) at kernel/qcoreapplication.cpp:587
    #13 0x00002afdc6c9718b in socketNotifierSourceDispatch (source=<value optimized out>) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:215
    #14 0x00002afdceb25a21 in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
    #15 0x00002afdceb287f4 in ?? () from /usr/lib/libglib-2.0.so.0
    #16 0x00002afdceb28ca8 in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
    #17 0x00002afdc6c972d5 in QEventDispatcherGlib::processEvents (this=0x66a940, flags=<value optimized out>) at kernel/qeventdispatcher_glib.cpp:327
    #18 0x00002afdc898bb2f in QGuiEventDispatcherGlib::processEvents (this=0x6db510, flags=<value optimized out>) at kernel/qguieventdispatcher_glib.cpp:204
    #19 0x00002afdc6c6d9a5 in QEventLoop::processEvents (this=<value optimized out>, flags=@0x7fffe43f0f70) at kernel/qeventloop.cpp:149
    #20 0x00002afdc6c6db08 in QEventLoop::exec (this=0x7fffe43f0fb0, flags=@0x7fffe43f0fc0) at kernel/qeventloop.cpp:200
    #21 0x00002afdc6c6fb0e in QCoreApplication::exec () at kernel/qcoreapplication.cpp:845
    #22 0x0000000000413923 in main (argc=1, argv=0x7fffe43f1398) at /home/lepagee/dev/tp3-prog_sess2/main.cpp:77
    Comme celle de KDebug disait, elle n'est pas tres utile. Sauf pour savoir que sa vient (comme previsiblement) du thread.

    Ma question portait plutôt sur le fait que tu construise une interface "à la volée" ou pas.
    Parfois, si sa amilore l'utilisabilitee.

    Un programme à interface en console envoie tout sur la sortie standard ou sur la sortie d'erreur. C'est son seul moyen d'écrire sur le terminal.
    Tu n'a aucun moyen sûr de savoir si un programme possède une telle interface. Tout ce que tu peux faire c'est essayer d'intercepter les certains codes de contrôle de du terminal (qui commencent par "\e[" avec \e qui représente le caractère de code 0x1b).
    1b5b3f31303439681b5b313b3131721b2842 plus presisement. Je comprend maintenan pourquoi elle ne semblais rien envoyer sur la sortie standard, c'est parce qu'elles n'emmetes pas de 0x0A comme les autres commande (meme celle a une seule ligne). Mon application ne les affichais donc pas. Bon, ca va etre sufisant pour les gerer. Mais la documentation de ca, c'est seulement celle du vt100 ou il y a d'autres protocols? Et aussi, il y a des exeptions, comme elinks qui elles, n'emetes vraiment rien dans les pipes.

    Renseigne-toi bien sur ce qui existe déjà avant de réinventer la roue carrée.
    Ah,ah, tres drole. Celui de gnome, kde, man2html et cie fonctionnent tous de la meme maniere, il ne sont pas capable d'extraire les informations, il ne font que les afficher. C'est dailleur aussi comme ca que je regle le probleme qu'est celui de maintenir l'index a jour, c'est ce parseur qui s'en occupe, il verifi lorsque la commande est appeler si la page a ete mise a jour, si oui, il met l'index des options pour cette commande a jour lui aussi. Comme ca je peux sans problemes tenir l'index des options a jour. Le seul probleme de ce principe, c'est que la man page doit etre plus ou moin conforme au model par defaut (name, synopsis, description, examples/usage, options, files, see also, author) ce qui est quand meme le cas pour une bonne partie.

    Il existe bashdb comme débuggeur de script shell bash.
    Je sais, j'ai fait des recherches. Il faut que je reecrive ce code de toute facon pour le terminal. Avant que tu me disent que je reecrit quelque chose qui existe, j'ai mes raisons de le faire, peut importe si je peut juste faire "sh <le code>", ca ete fait a une epoque ou les technologies etaient differente, ce n'est pas dans la meme direction que je veux pousser mon application, les besoins sont different. Des terminaux, il y en a assez comme d'identique comme ca. Celon moi, xterm est encore assez bon pour cette "generation" de terminaux. Pour ceux qui veulent plus lege et plus estetique, il y a (u)rxvt ou aterm. Pour ceux qui veulent quelque chose de mieux integrer a leur environement, il y a gnome-terminal, konsole et yakuake. Pour ceux sur windows, il y a cywing, putty et terraterm. Tous ceux la sont presque identique et fonctionne de la meme maniere. Ce n'est pas ce que je veux faire, c'est pour ca que je reecrit des choses qui date des annees 70/80/90. Je veux supporter ce qu'un terminal normal fait, mais je veux faire d'autres choses, meme si ca semble innutile, farfellue et cie, il faut essayer d'evoluer parfois, meme si au finale cela echou ou que c'etais un produit sans clientele cible, les idees qui y sont developpees, elles ont peut etre un avenir. Allors svp, arrete un peut de critiquer ce que je fais, meme si tu me vois comme un newb. Mon background n'est pas en programation systeme, non, mais sa ne veux pas dire que je suis un incompetent et que je ne sais pas travailler. Si je pose ces questions, ce n'est pas que je ne veux pas regarder dans les man, c'est que je ne sais pas quoi chercher.

    Le problème est toujours le même. Soit tu utilise un format facilement lisible par l'homme avec un simple éditeur de texte, mais plus difficile à lire par un programme, soit tu fais le contraire.
    Une base de donnees permet d'avoir le meileur des 2, a condition que tu ais une interface pour interagire avec. Ca me semble un bon compromis, si on prend en consideration que mon logiciel a cette interface en question.


    Mais peut importe, on s'eloigne du sujet. Mon projet n'a pas vraiment rapport avec ma questions actuel. Pour l'entree standard comment je fais pour savoir quelle demande quelque chose. Pour les timeout et tout ca, je suis capable d'envoyer un signal a la ligne de saisie qui est utiliser pour entrer le texte (ce que j'appelais ma ligne de commande) si quelque chose qui sort des pipe me dit de la faire.

    J'ai fait des testes avec printf("%x",buffer[i]); cat n'emet rien dans la sortie standard, ce qui me ramene a savoir comment je fais pour savoir qu'il y a une requete de saisie?

    EDIT: Je ne trouve pas grand chose sur le sujet, Re diriger le stdin sa se fait, mais savoir que le child le demande, je ne trouve rien la dessu... avec quelque testes, il semble que un terminal normal mette en cache toute les ligne de l'entree standard et le donne une par une quand le programme en a besoin. Si il sait quand les donner, il y a peut etre moyen, c'est tout ce que je trouve.

  18. #18
    Membre éprouvé
    Avatar de Celelibi
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    1 087
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 1 087
    Points : 1 122
    Points
    1 122
    Par défaut
    Citation Envoyé par Elv13 Voir le message
    La ligne ou on ecrit le commandes, c'est a quoi je fesais allusion.
    Ma remarque concernant les mots à utiliser était plus générale que ça. Quand tu emploie des termes techniques (genre "shell", "terminal", "sortie standard") assure-toi de les utiliser comme il faut afin de te faire comprendre et pour que les lecteurs ne passent pas trois heures à essayer de comprendre ce que tu as voulu dire.



    Citation Envoyé par Elv13 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    Program received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0x2afdcf5228c0 (LWP 27819)]
    QDBusConnectionPrivate::handleMessage (this=0x6db510, amsg=@0x7fffe43f04c0) at qdbusintegrator.cpp:488
    /home/kde-devel/kde/src/qt-copy/src/dbus/qdbusintegrator.cpp:488:17536:beg:0x2afdcceb779f
     
    #0  QDBusConnectionPrivate::handleMessage (this=0x6db510, amsg=@0x7fffe43f04c0) at qdbusintegrator.cpp:488
    #1  0x00002afdcceba64b in qDBusSignalFilter (connection=<value optimized out>, message=<value optimized out>, data=<value optimized out>) at qdbusintegrator.cpp:481
    #2  0x00002afdcfc6806f in dbus_connection_dispatch (connection=0x6df5d0) at dbus-connection.c:4267
    #3  0x00002afdcceaa989 in QDBusConnectionPrivate::doDispatch (this=0x6db510) at ./qdbus_symbols_p.h:100
    #4  0x00002afdcceaac16 in QDBusConnectionPrivate::socketRead (this=0x6db510, fd=14) at qdbusintegrator.cpp:1044
    #5  0x00002afdccee542a in QDBusConnectionPrivate::qt_metacall (this=0x6db510, _c=QMetaObject::InvokeMetaMethod, _id=4, _a=0x7fffe43f07b0) at .moc/release-shared/moc_qdbusconnection_p.cpp:88
    #6  0x00002afdc6c825cc in QMetaObject::activate (sender=0x6e1c20, from_signal_index=<value optimized out>, to_signal_index=4, argv=0x2afdcfc852d9) at kernel/qobject.cpp:3010
    #7  0x00002afdc6cbb3fe in QSocketNotifier::activated (this=0x6db510, _t1=14) at .moc/release-shared/moc_qsocketnotifier.cpp:81
    #8  0x00002afdc6c887ff in QSocketNotifier::event (this=0x6e1c20, e=0x7fffe43f0d70) at kernel/qsocketnotifier.cpp:326
    #9  0x00002afdc8905cee in QApplicationPrivate::notify_helper (this=0x66aa00, receiver=0x6e1c20, e=0x7fffe43f0d70) at kernel/qapplication.cpp:3772
    #10 0x00002afdc890a8ce in QApplication::notify (this=0x7fffe43f1060, receiver=0x6e1c20, e=0x7fffe43f0d70) at kernel/qapplication.cpp:3739
    #11 0x00002afdc9eeb210 in KApplication::notify (this=0x7fffe43f1060, receiver=0x6e1c20, event=0x7fffe43f0d70) at /home/kde-devel/kde/src/kdelibs/kdeui/kernel/kapplication.cpp:311
    #12 0x00002afdc6c6e588 in QCoreApplication::notifyInternal (this=0x7fffe43f1060, receiver=0x6e1c20, event=0x7fffe43f0d70) at kernel/qcoreapplication.cpp:587
    #13 0x00002afdc6c9718b in socketNotifierSourceDispatch (source=<value optimized out>) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:215
    #14 0x00002afdceb25a21 in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
    #15 0x00002afdceb287f4 in ?? () from /usr/lib/libglib-2.0.so.0
    #16 0x00002afdceb28ca8 in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
    #17 0x00002afdc6c972d5 in QEventDispatcherGlib::processEvents (this=0x66a940, flags=<value optimized out>) at kernel/qeventdispatcher_glib.cpp:327
    #18 0x00002afdc898bb2f in QGuiEventDispatcherGlib::processEvents (this=0x6db510, flags=<value optimized out>) at kernel/qguieventdispatcher_glib.cpp:204
    #19 0x00002afdc6c6d9a5 in QEventLoop::processEvents (this=<value optimized out>, flags=@0x7fffe43f0f70) at kernel/qeventloop.cpp:149
    #20 0x00002afdc6c6db08 in QEventLoop::exec (this=0x7fffe43f0fb0, flags=@0x7fffe43f0fc0) at kernel/qeventloop.cpp:200
    #21 0x00002afdc6c6fb0e in QCoreApplication::exec () at kernel/qcoreapplication.cpp:845
    #22 0x0000000000413923 in main (argc=1, argv=0x7fffe43f1398) at /home/lepagee/dev/tp3-prog_sess2/main.cpp:77
    Comme celle de KDebug disait, elle n'est pas tres utile. Sauf pour savoir que sa vient (comme previsiblement) du thread.
    Je ne connais pas du tout la structure de ton code, j'aurai donc du mal à interpréter la backtrace. Et puis, ne met pas d'optimisation pendant le développement, ça ne sert à rien, ça rend juste le débuggage plus difficile. Qu'on se mette bein d'accord ici aussi, la backtrace, c'est l'ensemble des fonctions appelées. Si il te dit "no backtrace" c'est qu'il a pas affiché la backtrace...


    Citation Envoyé par Elv13 Voir le message
    1b5b3f31303439681b5b313b3131721b2842 plus presisement.
    Cette séquence est à mon avis trop spécifique pour que tu puisse te baser dessus. Elle correspond en ascii à :
    Ça dit que le terminal doit switcher en mode 1049 (c'est un mode privé non ansi). Ensuite le scolling est activé entre les lignes 1 et 11. Enfin le "\e(B" indique de passer en mode USASCII.

    Citation Envoyé par Elv13 Voir le message
    Je comprend maintenan pourquoi elle ne semblais rien envoyer sur la sortie standard, c'est parce qu'elles n'emmetes pas de 0x0A comme les autres commande (meme celle a une seule ligne).
    Ton programme ne devrait pas se baser sur la présence d'un caractère. Pour la simple et bonne raison qu'il y a peut-être un 0x0a, mais qu'il apparaît assez loin. De plus, comment détermine-tu sa présence ? Tu met tout dans un buffer pour voir si tu trouve un te caractère ? C'est une très mauvaise manière de procéder, comme dit tu sur la première page tu devrai traiter le flux à la volée.

    Citation Envoyé par Elv13 Voir le message
    Mais la documentation de ca, c'est seulement celle du vt100 ou il y a d'autres protocols? Et aussi, il y a des exeptions, comme elinks qui elles, n'emetes vraiment rien dans les pipes.
    Regarde du côté des codes ansi. Pour ce qui est de elinks, il fait comme si l'option -dump lui était passée si sa sortie standard le correspond pas à un terminal.


    Citation Envoyé par Elv13 Voir le message
    Ah,ah, tres drole. Celui de gnome, kde, man2html et cie fonctionnent tous de la meme maniere, il ne sont pas capable d'extraire les informations, il ne font que les afficher.
    Et ils n'utilisent pas une librairie que tu pourrai réutiliser ?

    Citation Envoyé par Elv13 Voir le message
    Je sais, j'ai fait des recherches. Il faut que je reecrive ce code de toute facon pour le terminal.
    Encore une fois, tu n'est pas en train de coder un terminal.
    Un terminal c'est ça http://en.wikipedia.org/wiki/Image:T...25Terminal.jpg ; c'est un matériel.
    Les moyens matériels ayant évolués, les terminaux ont été virtualisés, c'est à dire que ce ne sont plus des matériels, mais ce sont des programmes (qu'on appel terminaux virtuels). Un terminal virtuel peut ressembler à ça http://en.wikipedia.org/wiki/Image:Knoppix-3.8-boot.png ou à ça http://fr.wikipedia.org/wiki/Image:Xterm_45.png
    Mais ils ont quand même la même fonction que les terminaux matériels de départ, c'est à dire afficher principalement du texte envoyé par le système, et envoyer au système des données entrées par l'utilisateur.
    Il me semble que ceci, je l'ai déjà expliqué dans un des premier messages de cette discussion. Tu es prié d'en ternir compte, MERCI !


    Donc, tu connais bashdb, mais tu n'as pas essayé de simplement regarder si le programme utilise une librairie que tu pourrai réutiliser ? Tu n'as pas essayé d'aller modifier le code pour en changer juste l'interface utilisateur ? Ou encore simplement reprendre des morceaux de code pour les intégrer à un projet à toi ?



    Citation Envoyé par Elv13 Voir le message
    Avant que tu me disent que je reecrit quelque chose qui existe, j'ai mes raisons de le faire, peut importe si je peut juste faire "sh <le code>", ca ete fait a une epoque ou les technologies etaient differente, ce n'est pas dans la meme direction que je veux pousser mon application, les besoins sont different. Des terminaux, il y en a assez comme d'identique comme ca.
    Si tu as bien compris ce qu'est un terminal, tu verra qu'il ne peut pas y en avoir de vraiment différents.

    Citation Envoyé par Elv13 Voir le message
    Celon moi, xterm est encore assez bon pour cette "generation" de terminaux. Pour ceux qui veulent [...snip...]. Tous ceux la sont presque identique et fonctionne de la meme maniere. Ce n'est pas ce que je veux faire, c'est pour ca que je reecrit des choses qui date des annees 70/80/90.
    Les terminaux matériels datent certes des années 70, mais les terminaux virtuels datent des années 80 pour les plus anciens (pas forcément bien conçu à cause du manque de standardisation dans les interfaces graphiques). Dans tous les cas tu peux être sûr que les termiaux virtuels continuent à être développés aujourd'hui encore.

    Citation Envoyé par Elv13 Voir le message
    Je veux supporter ce qu'un terminal normal fait, mais je veux faire d'autres choses,
    Ouch, là ça se complique. Je ne penses pas que tu ais actuellement assez de recule ni une vision assez globale des technologies pour pouvoir réellement innover.
    Sache tout d'abord qu'il existe depuis les années 80 des systèmes capable de d'afficher et demander graphiquement des informations. Ils sont généralement basés sur un serveur graphique X.
    Oui je parle bien entendu d'une interface graphique comme celle que tu utilises certainement actuellement.
    Ces systèmes possèdent toutes les fonctionnalités des terminaux, comme le fait de pouvoir exécuter un programme sur une machine distante et avoir un rendu de l'affichage directement sur sa propre machine (cf l'export display).
    Simplement, les entrées/sorties demandent d'être programmées différemment. Ce ne sont en effet plus des printf/scanf mais des appels à une librairie capable de dessiner des choses basiques.
    Toi, ce que tu voudrai faire, c'est un lien entre les deux, c'est ça ?
    Une sorte de wrapper qui fait correspondre des affichages graphiques à des entrées/sorties de terminal effectuées par le programme.


    Citation Envoyé par Elv13 Voir le message
    Si je pose ces questions, ce n'est pas que je ne veux pas regarder dans les man, c'est que je ne sais pas quoi chercher.
    Si tu ne sais pas quoi chercher, c'est que tu n'as pas bien défini ce que tu veux faire.


    Citation Envoyé par Elv13 Voir le message
    Une base de donnees permet d'avoir le meileur des 2, a condition que tu ais une interface pour interagire avec. Ca me semble un bon compromis, si on prend en consideration que mon logiciel a cette interface en question.
    Ça rentre donc totalement dans la catégorie des formats faits pour être lus uniquement par un programme spécifique.


    Citation Envoyé par Elv13 Voir le message
    Pour l'entree standard comment je fais pour savoir quelle demande quelque chose.
    C'est ce que j'essaye de te dire depuis le début.
    1) Il n'y a pas à ma connaissance de moyen d'obtenir cette information.
    2) Même si tu pouvait savoir à un moment donné si le programme attend des données, cette situation peut évoluer sans avoir besoin de l'interaction de l'utilisateur.

    Citation Envoyé par Elv13 Voir le message
    avec quelque testes, il semble que un terminal normal mette en cache toute les ligne de l'entree standard et le donne une par une quand le programme en a besoin. Si il sait quand les donner, il y a peut etre moyen, c'est tout ce que je trouve.
    En effet, en mode "cooked" le terminal va stocker les données entrées par l'utilisateur et les envoyer ligne par ligne, en mode "raw" tout est passé directement.
    Tu devrais peut-être plutôt créer des device de terminaux quand tu lance une application. Le programme screen le fait bien. Tu aurais à mon avis moins de mal à émuler certaines fonctionnalités en faisant ça.
    Si tu fais comme ça, là pour le coup tu pourra dire que tu code un truc qui est à moitié un terminal (du côté des programmes c'est un terminal classique alors que de l'autre côté c'est une GUI).
    Les vaches ne peuvent PAS voler, quoi qu'elles aient pu vous raconter.

Discussions similaires

  1. [Batch] Comment passer le résultat d'une commande en variable
    Par Ohemeg dans le forum Scripts/Batch
    Réponses: 3
    Dernier message: 30/11/2009, 23h51
  2. Réponses: 4
    Dernier message: 22/08/2008, 09h43
  3. Comment afficher le rang des résultats d'une requête sous Access 2002
    Par painpepper dans le forum Requêtes et SQL.
    Réponses: 5
    Dernier message: 15/08/2007, 01h19
  4. [SQL] Comment je peux mettre les résultat d'une requete dans un fichier
    Par Maria1505 dans le forum PHP & Base de données
    Réponses: 10
    Dernier message: 10/12/2006, 21h44
  5. Réponses: 2
    Dernier message: 04/10/2006, 14h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo