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

C Discussion :

Mini-shell probleme avec pipe


Sujet :

C

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut Mini-shell probleme avec pipe
    Bonjour,

    Je suis débutant en c. J'ai écrit un mini-shell en c que je souhaite compléter, pouvant ainsi utiliser des pipes. Seulement voilà, le code c semble fonctionner puisqu'il m'affiche la bonne réponse, mais aussi-tôt une commande lancée avec un pipe, je suis éjecté du mini-shell. Je galère depuis une bonne semaine sur ce problème sans pouvoir trouver une solution.

    Ci-après mon code:
    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
    int main(int argc, char * argv[])
    {  	decouper(getenv("PATH"), ":", dirs, MaxDirs);			
      	for(printf(PROMPT); fgets(ligne, sizeof ligne, stdin) != 0; printf(PROMPT))
    	{	decouper(ligne, " \t\n", mot, MaxMot);				
    		if (mot[0] == 0)            						
          	continue;	
    		if (strcmp(mot[0], "moncd") == 0)					
    		{	if (mot[1] == NULL)								
    			{	dir = getenv("HOME");
    				if (dir == 0)
    				{	dir = "/tmp";
    				}
    			}
    			else if (mot[2] != NULL)						
    			{	fprintf(stderr, "usage: %s [dir]\n", mot[0]);
    				continue;
    			}	
    			else dir = mot[1];								
    			t = chdir(dir);
    			if (t < 0)
    			{	perror(dir);
    			}	
    			continue;
    		}
    		for (cmp = 0; mot[cmp]; cmp++)
    		{	if(mot[cmp] != NULL && strcmp(mot[cmp], tube) == 0)
    			{	mot[cmp] = NULL;
    				mot2[0] = mot[cmp + 1];
    				mot2[1] = NULL;
    				if (pipe(pfd) == -1)
    				{	perror("pipe");
    					continue;
    				}
    				if ((tmp = fork()) < 0)
    				{	perror("fork");
    					continue;
    				}
    				if (tmp == 0)
    				{	close(STDOUT_FILENO);
    					dup(pfd[1]);
    					close(pfd[0]);
    					close(pfd[1]);
    					for(i = 0; dirs[i] != 0; i++)
    					{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot[0]);
          					execv(pathname, mot);
    					}
    				}
    				else
    				{	close(STDIN_FILENO);
    					dup(pfd[0]);
    					close(pfd[0]);
    					close(pfd[1]);
    					for(i = 0; dirs[i] != 0; i++)
    					{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot2[0]);
          					execv(pathname, mot2);
    					}
    				}
    			}
    		}
     
    		verif_esperluette();						
    		verif_redirection();						
     
    		tmp = fork();               
        	if (tmp < 0)
    		{	perror("fork");
          		continue;
        	}
     
     
    		if ((tmp != 0) && (test == 1))
    		{	while(wait(0) != tmp);
    			continue;
    		}
    		if((tmp != 0) && (test == 0))
    		{	continue;
    		}
     
     
    		if (fichier_1 == 1)
    		{	out = freopen(path_out, "w+", stdout);
    		}
    		if ((fichier_1 == 1) && (fichier_2 == 1))
    		{	in = freopen(path_in, "r", stdin);
    			out = freopen(path_out, "w+", stdout);
    		}
    		if (fichier_2 == 1)
    		{	in = freopen(path_in, "r", stdin);
    		}
     
     
        	for(i = 0; dirs[i] != 0; i++)
    		{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot[0]);
          		execv(pathname, mot);
        	}
     
    		fprintf(stderr, "%s: not found\n", mot[0]);
    	    exit(1);
      	}
      	printf("Bye\n");
      	return 0;
    }
    Est-ce que quelqu'un à une idée?
    Merci d'avance.

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Points : 1 750
    Points
    1 750
    Par défaut
    Salut

    C'est totalement illisible. Edite ton message pour indenter correctement ton code et le mettre entre balises "CODE" prévues à cet effet.

  3. #3
    Membre éprouvé
    Avatar de f-k-z
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2006
    Messages
    403
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2006
    Messages : 403
    Points : 928
    Points
    928
    Par défaut
    Yopyop,

    Bon, il est tard et l'indentation n'est pas des plus classiques notamment pour l'utilisation des { , mais bon ceci n'est qu'un détail et une question d'habitude :p

    J'ai regardé ton code et il me semble que c'est à toi d'utiliser une fonction pipe().

    Je regarde dans des archives, je dois avoir un truc du genre.

    //edit:
    Issu du man pipe(2). Je te laisse explorer cette piste

    Citation Envoyé par man pipe(2)
    The following program creates a pipe, and then fork(2)s to create a child process. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.
    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
    #include <sys/wait.h>
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    int
    main(int argc, char *argv[])
    {
        int pfd[2];
        pid_t cpid;
        char buf;
        assert(argc == 2);
        if (pipe(pfd) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
        cpid = fork();
        if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); }
        if (cpid == 0) {    /* Child reads from pipe */
            close(pfd[1]);          /* Close unused write end */
            while (read(pfd[0], &buf, 1) > 0)
                write(STDOUT_FILENO, &buf, 1);
            write(STDOUT_FILENO, "\n", 1);
            close(pfd[0]);
            _exit(EXIT_SUCCESS);
        } else {            /* Parent writes argv[1] to pipe */
            close(pfd[0]);          /* Close unused read end */
            write(pfd[1], argv[1], strlen(argv[1]));
            close(pfd[1]);          /* Reader will see EOF */
            wait(NULL);             /* Wait for child */
            exit(EXIT_SUCCESS);
        }
    }

  4. #4
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Merci Yopyop pour ton poste,

    Mais j'avais déjà regardé sous man pipe.

    Seulement voilà le programme affiche le bon résultat, mais ne reste pas dans la boucle. Je ne sais pas trop d'où provient le problème puisqu'il m'affiche correctement le résultat de "ls | wc" seulement après il m'éjecte.

    J'ai un doute et je me demande s'il ne faut pas refaire un 2ième fork() après que l'enfant ait exécute la première commande. (ceci étant dit je l'ai également déjà tenté, mais sans réussir).

    Merci d'avance pour tout proposition.

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 809
    Points
    23 809
    Par défaut
    Je n'ai lu ton code qu'en diagonale, mais ceci me laisse perplexe :

    Citation Envoyé par Appsolu 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
    int main(int argc, char * argv[])
    {
    []
    				if ((tmp = fork()) < 0)
    				{	perror("fork");
    					continue;
    				}
    				if (tmp == 0)
    				{
    []
    					for(i = 0; dirs[i] != 0; i++)
    					{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot[0]);
          					execv(pathname, mot);
    					}
    				}
    				else
    				{
    []
    					{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot2[0]);
          					execv(pathname, mot2);
    					}
    				}
    Tu fais des execv() des deux côtés : dans le processus père et dans son fils.

  6. #6
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Bonjour Obsidian,

    Merci pour ton poste.

    Oui effectivement je fais des execv(). Est-ce qu'il ne faut pas exécuter une première partie de la commande et puis la deuxième?

    Dans le cas de "ls | wc" je pensait exécuter ls puis envoyer le résultat à wc?

    Comment procéder alors? je suis désespère, celà fas plus d'une semaine que je passe dessus, sans résultat.

    Merci à vous.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 809
    Points
    23 809
    Par défaut
    Ce n'est pas le fait de faire execv() qui pose problème, c'est le fait de le faire des deux côtés.

    La famille des exec() remplace l'image du processus en cours par celle qu'il va charger depuis ton exécutable. C'est précisément pour cela que l'on fait un fork() avant : parce que le processus fils va devenir celui qu'on va lancer.

    Si tu fais execv() depuis le père, tu écrases ton mini-shell.

  8. #8
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Salut Obsidian

    Merci pour ton commentaire, je vais revoir mon script. Je te tiens au courant de l'évolution de mon problème.

    En attentant encore merci de m'aider, c'est pas toujours évident quand ont est seul et débutant. Je commence a regagner espoir

    A+

  9. #9
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Bon, c'est pas gagné.

    J'ai suivi ton commentaire Obsidian et je n'arrête pas de tourner le tout. J'ai simplifié ma fonction de façon a vérifier si j'arrive au moins à implémenter le code correcte pour les pipes mais malheureusement sans aboutir.

    Voilà ou j'en suis:
    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
    int main(int argc, char * argv[])
    {  	decouper(getenv("PATH"), ":", dirs, MaxDirs);			
      	for(printf(PROMPT); fgets(ligne, sizeof ligne, stdin) != 0; printf(PROMPT))
    	{	decouper(ligne, " \t\n", mot, MaxMot);				
    		if (mot[0] == 0)            						
          	continue;	
    		int pfd[2];
    		char buf[1024];
    		pipe(pfd);
    		int pid;
    		pid = fork();
    		if (pid != 0)
    		{	close(pfd[1]);
    			while (wait(0)!= pid);
    			read(pfd[0], buf, sizeof(buf));
    			printf("read %s\n", buf);
    			continue;
    		}
    		else if (pid == 0)
    		{	close(pfd[0]);
    			close(1);
    			dup(pfd[1]);
    			close(pfd[1]);
    			for(i = 0; dirs[i] != 0; i++)
    			{	snprintf(pathname, sizeof pathname, "%s/%s", dirs[i], mot[0]);
          			execv(pathname, mot);
    			}
    		}
     
    		fprintf(stderr, "%s: not found\n", mot[0]);
    	    exit(1);
      	}
      	printf("Bye\n");
      	return 0;
    }
    Et voilà le message d'erreur que j'obtiens:

    ? ls wc
    ls: ne peut accéder wc: Aucun fichier ou dossier de ce type
    read t
    ?

    Du moins maintenant je reste dans la boucle du mini shell.

    Pour résumer je ne comprend vraiment pas ce qui bloque, si quelqu'un à une idée resp. proposition, je suis preneur.

    Merci à vous.

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 809
    Points
    23 809
    Par défaut
    Citation Envoyé par Appsolu Voir le message
    Pour résumer je ne comprend vraiment pas ce qui bloque, si quelqu'un à une idée resp. proposition, je suis preneur. Merci à vous.
    Ça ne bloque pas : ça fonctionne (presque) normalement.
    — « ls: ne peut accéder wc: Aucun fichier ou dossier de ce type » est le message-type renvoyé par ls quand il ne trouve pas un fichier. Ça veut donc dire que tu as réussi à l'exécuter ;
    — « read t » est le message que tu affiches en ligne 30. Ton printf() est indépendant de ton if(). Comme le processus père ne sera pas remplacé, il va forcément tomber dessus quoi qu'il se passe.
    — Ce qu'il faut faire maintenant (à l'intérieur du if), c'est faire en sorte que le père attende la fin du processus fils avec wait().

  11. #11
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Salut Obsidian,

    Tu dis qu'il faut faire un wait() à l'intérieur du if, mais il y est déjà !

    Ou bien tu fais référence à quelque chose du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    if (pid != 0)
    		{	close(pfd[1]);
    			while (wait(0)!= pid);
    			while (read (pfd[0], buf, sizeof(buf)) != 0)
    				printf("read %s\n", buf);
    			continue;
    		}
    Mais le message d'erreur reste le même:
    ? ls wc
    ls: ne peut accéder wc: Aucun fichier ou dossier de ce type
    ?

  12. #12
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 809
    Points
    23 809
    Par défaut
    Oups ! Effectivement, j'ai lu beaucoup trop vite. Au temps pour moi. Bon, quelques remarques :

    — Il ne faut pas écrire wait(0) mais wait(NULL). Mais ceci ne changera pas grand chose dans les faits ;
    — Il faut mettre le contenu de ton buffer « buf » à zéro avec memset() pour éviter les désagréments que tu rencontres. D'autant plus que read() n'ajoutera pas de zéro final ;
    — Le message « ls: ne peut accéder wc: Aucun fichier ou dossier de ce type » est un message d'erreur. Il est donc envoyé sur stderr plutôt que sur stdout. C'est pour cela que tu ne l'interceptes pas. Essaie de remplacer « wc » par un fichier existant dans le répertoire courant et cela marchera bien mieux.
    — Les buffers sont censés être vidés à la mort du processus, mais dans ton cas, tu commences à lire alors que le processus est déjà mort. Ça pourrait poser problème. À la place, il faut faire une boucle qui fait read successivement et qui, à chaque fois, dépose le contenu dans le buffer à la suite du reste, et qui ne s'arrête que lorsque read a renvoyé 0.

    Habituellement, read nous renvoie soit le nombre d'octets effectivement lus, soit -1 en cas d'erreur. S'il renvoie 0, c'est que le tube ou le socket lu a été proprement refermé par l'écrivain à l'autre extrémité, ou que l'on a atteint la fin du fichier si c'est cela qu'on était en train de lire.

    Et seulement ensuite, tu fais un wait() pour que le père prenne acte de la mort de son fils. Ce n'est qu'à ce moment que celui-ci cessera d'être un zombie et que son âme pourra, paisible, s'envoler au ciel. :-)

Discussions similaires

  1. Creation d'un shell probleme avec execve
    Par Mouch2 dans le forum Débuter
    Réponses: 3
    Dernier message: 07/01/2011, 18h37
  2. script shell : probleme avec sed et awk
    Par salseropom dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 05/06/2007, 12h57
  3. [VC++] probleme avec le Pipe Named
    Par ksoft dans le forum Visual C++
    Réponses: 2
    Dernier message: 21/07/2006, 19h34
  4. Problème avec mon script en Shell
    Par G.D.O dans le forum Linux
    Réponses: 8
    Dernier message: 05/07/2006, 17h40
  5. [VBscript] probleme avec shell.exec
    Par eown dans le forum Windows
    Réponses: 1
    Dernier message: 23/04/2006, 10h24

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