Bonjour, je suis en train de faire l'exercice suivant :

Exercice 3 : tâches de fond¶

Dans un shell classique, on peut lancer une commande en tâche de fond, en particulier lorsque l’on ne souhaite pas mono*poliser l’interpréteur pour cette commande, et que l’on veut pouvoir lancer d’autres commandes en parallèle. Pour cela, on fait suivre la ligne de commande d’un &.

Il s’agit maintenant d'écrire un programme shell3, basé sur le shell2 du TP3, intégrant en plus la possibilité de lancer des commandes en tâche de fond, avec une syntaxe différente de celle des shells classiques :

Lorsque la ligne de commande saisie commence par le mot tf (tâche de fond), alors la commande suivant tf sera lancée dans un nouveau processus, mais votre shell n’attendra pas la fin de l’exécution et il ré*affichera immédiatement l'invite de saisie pour saisir et exécuter la commande suivante.

Lorsque la ligne de commande saisie ne commence pas par le mot tf, la commande correspondante sera exécutée, et votre programme attendra la fin de l’exécution pour ré*afficher l'invite de saisie (même com*portement que dans le TP3).

Attention encore à ne pas créer de zombies...

Vous ferez en sorte que, lorsqu’une commande lancée en tâche de fond se termine, votre shell affiche un message indiquant la fin du processus (avec son numéro de pid).
Voilà 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
 
#include <stdio.h>
#include <unistd.h>
#include "ligne_commande.h"
#include <sys/types.h> 
#include <sys/wait.h> 
#include <string.h>
#include <stdlib.h>
 
int commandes_internes (char** cmd );
 
int main()
{
	char** commande;
	int status;
	int nb;
	setenv("INVITE", "\n> ", 1);
	do
	{
	printf("%s", getenv("INVITE"));
	fflush(stdout);
	commande=lis_ligne();
 
	if(commande==NULL) return 0;
	if( !commandes_internes(commande) ) //commande externe
		{	
		int pid=fork();
		if(pid==0)
		{
			if(strcmp(commande[0],"tf")!=0) nb=execvp(commande[0],commande);
			else nb=execvp(commande[1],commande+1);
			if(nb==-1) printf("Cette commande n'existe pas.\n");fflush(stdout);
		}
		else if (pid==-1)
		{
			perror("Erreur de l'appel systeme fork");
			return(-1);
		}
		else
		{
 
			if(strcmp(commande[0],"tf")!=0)
				waitpid(pid,&status,0);
			printf("test5\n");
		}
		}
	//printf("fin_de_fichier(commande)=%d\n, ligne_vide(commande)=%d",fin_de_fichier(commande),ligne_vide(commande));
	} while (fin_de_fichier(commande)==0 || ligne_vide(commande)==1);
	if(strcmp(commande[0],"tf")==0) printf("Fin du processus en tache de fond de numero %d",getpid());
	return 0;
}
 
int commandes_internes (char** cmd ){
 
	char* droite;
	int nb;	
	if( strcmp(cmd[0],"exit") == 0 )  //strcmp compare 2 chaînes de caractère et renvoie 0 si elles sont identiques
	{
		printf("fin du programme\n");
		exit(0); //fin du programme
	}
 
 
	if( strcmp(cmd[0],"cd")==0 ) 
	{
		if(cmd[1])
		{
			nb=chdir(cmd[1]);
			if(nb==-1) perror("Erreur de la commande chdir.");
		}
		else printf("Syntaxe : cd argument");
		return 1;
	}
 
	if( strcmp(cmd[0],"export")==0 )
	{	
		if(cmd[1])
		{
		droite=separe_egal(cmd[1]);
		if(droite==NULL) printf("La commande export n'a pas de signe egal.");
		nb=setenv(cmd[1], droite, 1);
		if(nb==-1) perror("Il n'y a plus de place pour cette variable d'environnement.");
		}
		else
			printf("Syntaxe : export arg1=arg2");
		return 1;
 
	}
 
	return 0;
 
}
J'ai fait un Makefile avec les fichiers fournis :

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
 
#ifndef _LIGNE_COMMANDE_V2_H
#define _LIGNE_COMMANDE_V2_H
 
/**
 * Cette fonction lit sur l'entrée standard une ligne de texte,
 * la découpe selon les espaces, et retourne un tableau de char*
 * du type attendu par execv et execvp.
 *
 * Si la fin de fichier est rencontrée, cette fonction retourne NULL.
 *
 * Voir aussi les fonctions fin_de_fichier et ligne_vide.
 */
 
char** lis_ligne();
 
/**
 * Cette fonction prend en paramètre le tableau de char* retourné par lis_ligne
 * et retourne vrai si et seulement si lis_ligne a rencontré la fin de fichier.
 * (c.à.d. si ligne est NULL)
 */
int fin_de_fichier(char** ligne);
 
/**
 * Cette fonction prend en paramètre le tableau de char* retourné par lis_ligne
 * et retourne vrai si la ligne lue était vide.
 * Pré-condition: on n'est pas en fin de fichier, ligne != NULL
 */
int ligne_vide(char** ligne);
 
 
/**
 * Cette procédure coupe une chaîne de caractères en deux à l'endroit du signe
 * égal, de sorte qu'après son exécutuion:
 *  - la chaîne passée en paramètre ne contient plus que sa partie gauche,
 *  - le pointeur retourné pointe vers la partie droite.
 *
 * Si la chaîne passée en paramètre ne contient pas le signe égal, la chaîne
 * n'est pas modifiée, et la fonction retourne NULL.
 */
char* separe_egal(char* str);
 
 
#endif
Fichier ligne_commande.c :
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
 
#include "ligne_commande.h"
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
#define LBUF 1024
#define LLIGNE 256
static char buf[LBUF];
static char* ligne[LLIGNE];
 
char** lis_ligne()
{
    int i = 0;
    int s;
    do
    {
        s = read(0, buf+i, 1);
        i += s;
    } while (i < LBUF-1  &&  s > 0  &&  buf[i-1] != '\n');
    if (s < 0) perror("erreur de lecture dans lis_ligne");
    if (i == 0) return NULL;
 
    // découpage
    char* tok;
    buf[i] = '\0';
    i = 0;
    ligne[0] = strtok_r(buf, " \t\n\r", &tok);
    while (ligne[i] && i < LLIGNE-1)
    {
        i += 1;
        ligne[i] = strtok_r(NULL, " \t\n\r", &tok);
    }
    if (i == LLIGNE-2) ligne[LLIGNE-1] = NULL;
    return ligne;
}
 
int fin_de_fichier(char** ligne)
{
    return ligne == NULL;
}
 
int ligne_vide(char** ligne)
{
    return ligne[0] == NULL;
}
 
char* separe_egal(char* str)
{
  char* p = str;
  while (*p) {
    if (*p == '=') {
      *p = '\0';
      return p+1;
    }
    p++;
  }
  return NULL;
}
Est-ce que vous pouvez me dire si mon code est correct et permet bien d'exécuter une commande en tâche de fond ? Et comment faire pour afficher un message lorsque le processus en tâche de fond est terminé ? Merci !