Partage
  • Partager sur Facebook
  • Partager sur Twitter

Chdir et fork

    15 janvier 2023 à 23:13:23

    Bonjour, j'essaie de re coder la fonction cd en utilisant chdir et getenv

    static
    void	change_directory(const char *path)
    {
    	if (chdir(path) == -1)
    		perror(path);
    }
    
    void	cd(const char *path)
    {
    	if (!path || ft_strcmp(path, "~") != 0)
    		change_directory(getenv("HOME"));
    	else
    		change_directory(path);
    }

    Mais dans un fork(), ca ne fonctionne pas, savez vous pourquoi svp ?

    • Partager sur Facebook
    • Partager sur Twitter
      16 janvier 2023 à 11:26:18

      Bonjour,

      Je ne comprends pas "Dans un fork()". Ou bien la fonction est appelée avant le fork() et le process fils aura le même contexte donc aura changé de répertoire. Ou bien c'est après le fork() et là bien évidemment les process étant séparés, tout changement de répertoire du père ou du fils n'est pas fait dans l'autre.

      Et en supposant que ft_strcmp() fait la même chose que strcmp(), ta fonction cd() ne sait aller que sur le répertoire home. Il faut plutôt écrire:

      if ( !path  ||  ft_strcmp(path, "~") == 0 )     // si le path es NULL ou bien le path est "~"
      • Partager sur Facebook
      • Partager sur Twitter

      En recherche d'emploi.

        17 janvier 2023 à 7:47:01

        Bonjour,

        Au fait, si je le lance dans un fork c'est parce que j execute des commandes avec execve mais peut etre que je dois pas fork cd pour cd ? Il y a aucune commande qui bash qui peut faire en sorte que j'ai a fork ?

        Et effectivement je me suis trompe sur le retour de ft_strcmp, merci.

        Et aussi autre question, const char * et char const * et const char * const, on est d'accord que les trois c'est la meme chose il y a aucune difference en C?

        Et aussi, j'aimerai implementer les here doc, j'ai une solution qui consiste a creer un fichier mais j'aime pas trop cette solution car si quelqu'un s'amuse a chmod le fichier ou le creer juste avant moi je ne pourrai pas ecrire dedans temporairement, j'ai penser a un coup de marteau, boucle tant que nom_du_fichier  + i existe mais la c'est vraiment pas top top, j'ai vu qu'il y avait un flag O_TEMP mais j'arrive pas a y acceder. Il y a aucun moyen de faire autrement ?

        J'ai aussi tenter de reproduire la fonction exit sans option mais disons que c'est encore bancal, pouvez vous m'aider la dessus svp ?

        #define LLMAX	9223372036854775807LL
        #define LLMIN	-9223372036854775807LL
        
        void	is_exit(char **argument)
        {
        	const char	*tmp;
        	long long	res;
        	int			index;
        
        	if (!argument || !*argument)
        	{
        		ft_putendl("exit");
        		return ;
        	}
        	index = 0;
        	while (argument[++index])
        	{
        		tmp = argument[index];
        		res = ft_atoull(tmp); // long long int
        		if (*tmp == '+' || *tmp == '-')
        			tmp ++;
        		while (ft_isdigit(*tmp))
        			tmp++;
        		if (index > 1)
        		{
        			ft_putendl("exit");
        			ft_printf("bash: exit: too many arguments\n", tmp);
        			exit(1);
        		}
        		if (*tmp != '\0' || res > LLMAX || res < LLMIN)
        		{
        			ft_printf("bash: exit: %s: numeric argument required\n", tmp);
        			exit(2);
        		}
        	}
        	ft_putendl("exit");
        	if (argument[1])
        		exit(ft_atoull(argument[1]));
        	exit(EXIT_SUCCESS);
        }
        • Partager sur Facebook
        • Partager sur Twitter
          17 janvier 2023 à 8:35:26

          Une question à la fois, pour rester concentrés.

          • Un shell permet de lancer l'exécution de programmes, ce qu'on appelle commandes externes
          • Il traite aussi des commandes internes, comme exit, if, function, while case, etc

          Sous unix, le lancement des commandes externes se fait par une combinaison de fork,exec,wait,dup2 etc.

          La commande cd fait partie des commandes internes. 

          En gros le shell c'est une boucle qui 

          • Lit une commande
          • L'execute

          Pour exécuter on commence par regarder si c'est une des commandes internes. Si c'est exit, ça fait sortir de la boucle. Si c'est cd, ça appelle chdir.  Et si c'est une commande externe, ça l'appelle dans un processus fils à part.

          (Il y a aussi le cas des structures de contrôle, passons, on ne peut pas tout expliquer d'un coup)

          (L'autre jour j'ai commencé à écrire un petit document pédagogique  là dessus, faut que je le finisse. To be continued)

          Ps : tu devrais séparer l'analyse syntaxique de la ligne (Une commande simple est une suite de mots) de l'exécution.  Faire une fonction qui découpe la ligne.

          -
          Edité par michelbillaud 17 janvier 2023 à 8:51:07

          • Partager sur Facebook
          • Partager sur Twitter
            17 janvier 2023 à 9:30:25

            D'accord merci donc je dois pas fork en executant une commande interne.

            michelbillaud a écrit:

            Ps : tu devrais séparer l'analyse syntaxique de la ligne (Une commande simple est une suite de mots) de l'exécution.  Faire une fonction qui découpe la ligne.

             Si vous parlez du parametre de la fonction is_exit, je la stock deja separement. j'ai un double tableau pour ca qui me stock le nom de la command et les params/opt de la commande comme ca je peux l'envoyer a execve directement

            • Partager sur Facebook
            • Partager sur Twitter
              17 janvier 2023 à 12:37:58

              Oups j'ai écrit un peu vite

              Ca serait commode

              • que la fonction reçoive aussi le nombre d'arguments
              • comme ça il suffirait de tester qu'il y en a 0 ou 1 (ce qui servirait aussi pour cd)
              • et que si il y en a un, c'est une chaîne numérique (avec une fonction  bool is_a_number(char *) )
              PS: les conventions habituelles veulent quand le nom d'une fonction commence par is, ou has, elle retourne  un boolean.   Un nom comme execute_exit_command(), ça serait plus clair que is_exit()

              -
              Edité par michelbillaud 17 janvier 2023 à 12:39:40

              • Partager sur Facebook
              • Partager sur Twitter
                18 janvier 2023 à 2:43:09

                SkittlesLittle a écrit:

                Et aussi autre question, const char * et char const * et const char * const, on est d'accord que les trois c'est la meme chose il y a aucune difference en C?

                Le mot const peut être mis avant ou après le type, donc les 2 premiers sont bien la même chose. Pas le troisième, les 2 mots const indiquent que 2 choses sont constantes, il s'agit d'un pointeur constant sur des caractères constants. Pour les pour 2 premiers la variable n'est pas constante, elle pointe sur des caractères qui ne seront pas modifiés, mais peut être réaffectée pour pointer ailleurs.

                const int  I = 5;           // I vaudra toujours 5    (I=... interdit)
                char* const  Ptr = "";      // Ptr ne changera jamais (Ptr=... interdit)
                const char*  ptr;           // pointe sur du constant (*ptr=... interdit)
                const char* const  P2 = ""; // (ni P2=...  ni *P2=...)
                
                char const**const  PP = &ptr;
                PP = &ptr;       // interdit PP est constant (à cause du dernier const)
                *PP = ptr;       // oui *pp peut être modifié
                **PP = 'a';      // interdit **PP est constant (à cause du 1er const)
                • Partager sur Facebook
                • Partager sur Twitter

                En recherche d'emploi.

                  18 janvier 2023 à 11:37:12

                  Pour trouver une logique là dedans :

                  const char c = 'a';

                  déclare une variable c "en lecture seule", initialisée, dont le contenu ne peut être changé. Non modifiable.

                  Remarques :

                  • on peut intervertir const et le type
                  • on peut déclarer des champs const dans une structure. L'initialisation des champs constants devra être faite à l'initialisation
                  	struct truc {
                  		const char c;
                  	    int n;
                  	};
                  
                  	struct truc t = {
                  		.c = 'a',
                  		.n = 12
                  	};
                  
                  	// t.c = 'b';     NON
                  	t.n = 34;
                  Les champs d'une variable de type structure déclarée const sont logiquement en lecture seule.
                  	const struct truc tc ={
                  		.c = 'a',
                  		.n = 12
                  	};
                  
                  	// tc.n = 33;     NON 
                  }

                  ---


                  Bon, passons à plus rigolo, les "doubles pointeurs". Si on déclare

                  int **p; 


                  c'est pour avoir une variable qui contiendra l'adresse d'un pointeur vers un entier. Si on met const devant

                  const int **p;

                  qu'est-ce qui est const ?

                  Si on part de l'idée que p est de type "pointeur vers un pointeur d'entier", ça conduirait à la conclusion que p est en lecture seule. Et bien, perdu :

                  	const int **p;
                  	p = NULL;           // ça passe
                  

                  La vraie signification de la déclaration (que vos Profs de C Ne Veulent Pas que Vous Sachiez) :  **p est de type "entier en lecture seule". C'est pas pareil. Ce que ça empêche de faire, c'est ça

                  	//  **p = 12;     NON

                  On peut aussi intervertir le type (int) et const, avec le même effet :  int const ** p;

                  ---

                  Si on veut empêcher de modifier le double pointeur, il faut mettre const avant le nom

                  	int ** const q = NULL;
                  	// q = NULL;      NON
                  	*q = NULL;
                  	**q = 12;

                  Si on le met au milieu

                  int * const *r;

                  ça dit que r est un pointeur (modifiable) vers un pointeur (non modifiable) vers un entier (modifiable).

                  	int * const * r = NULL;
                  	r = NULL;          //        r est modifiable
                  	// *r = NULL;      // NON : *r en lecture seule
                  	**r = 12;          ///     **r est modifiable












                  -
                  Edité par michelbillaud 18 janvier 2023 à 11:38:56

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 janvier 2023 à 14:07:02

                    Merci pour vos precisions mais quelque chose me chagrine

                    michelbillaud a écrit:

                    Pour exécuter on commence par regarder si c'est une des commandes internes. Si c'est exit, ça fait sortir de la boucle. Si c'est cd, ça appelle chdir.  Et si c'est une commande externe, ça l'appelle dans un processus fils à part.

                     Prenons le cas de: cd .. | cd ..
                     en executant avant valgrind bash, on s apercoit qu'un proc a bien ete lance, devrais-je crer un proc enfant s'il y a plus d'une commande et si c'est une commande interne ?
                    Aussi, j'ai une derniere question, j'execute des forks a deux endroits, un pour les heredoc et l autre pour les procs, pour recuperer le bon status code, vous pensez qu'une variable globale fait l'affaire ou alors je me balade ma struct/variable un peu partout ?
                    Merci
                    • Partager sur Facebook
                    • Partager sur Twitter
                      19 janvier 2023 à 14:22:22

                      Valgrind, bash :

                      la page de manuel de bash dit que chaque commande d'un pipeline s'exécute dans un processus séparé. Si on se sert du manuel de bash comme spécification de ce qu'on veut faire, ben voilà. (À un moment il faut bien spécifier ce qu'on veut faire, avant de coder)

                      Heredocs, forks, Status, variable globale : desolé je ne vois pas de quoi il sagit. Je dirais que  l'exécution d'une commande peut modifier l'état du shell (penser aux affectations message="coucou" par exemple, mais aussi "retcode" de la derniere commande executee) et qu'il va donc falloir transmettre ce contexte aux fonctions qui exécutent les commandes. Ça répond peut etre pas à la question.

                      -
                      Edité par michelbillaud 19 janvier 2023 à 15:15:44

                      • Partager sur Facebook
                      • Partager sur Twitter
                        20 janvier 2023 à 0:55:40

                        SkittlesLittle a écrit:

                        Prenons le cas de: cd .. | cd ..

                        en executant avant valgrind bash, on s apercoit qu'un proc a bien ete lance, devrais-je crer un proc enfant s'il y a plus d'une commande et si c'est une commande interne ?

                        A propos de cd, si tu veux savoir pourquoi c'est une commande interne, même si cd peut aussi exister en commande externe, tu peux lire https://unix.stackexchange.com/questions/38808/why-is-cd-not-a-program

                        Mais l'explication la plus courte, c'est que si le shell fork la commande, alors ce que fait la commande n'a pas d'effet sur le shell. Le chdir() n'aura d'effet que dans le processus de la commande cd, mais pas dans le shell (qui est le processus parent).

                        • Partager sur Facebook
                        • Partager sur Twitter
                          21 janvier 2023 à 23:51:30

                          La fonction chdir() permet de changer le répertoire courant pour le processus en cours d'exécution. Lorsque vous utilisez cette fonction dans un processus enfant créé à l'aide de la fonction fork(), le changement de répertoire ne s'applique qu'au processus enfant et non au processus parent.

                          Lorsque vous utilisez la fonction fork(), un nouveau processus est créé à partir de celui qui l'appelle. Le processus enfant hérite de la plupart des ressources du processus parent, y compris le répertoire courant. C'est pourquoi, lorsque vous utilisez chdir() dans un processus enfant, le répertoire courant ne change pas pour le processus parent.

                          Pour résoudre ce problème, vous pouvez utiliser la fonction chdir() dans le processus parent après avoir créé le processus enfant. Vous pouvez également utiliser une variable d'environnement pour stocker le répertoire courant et la partager entre les processus parent et enfant.

                          Il est important de noter que le changement de répertoire ne modifie pas les variables d'environnements. Il est donc important de récupérer la valeur de PWD pour la stocker dans une variable d'environnement pour pouvoir la réutiliser dans les processus enfants.

                          Voici un exemple de code qui utilise une variable d'environnement pour stocker le répertoire courant et le partager entre les processus parent et enfant :

                          #include <stdio.h>
                          #include <stdlib.h>
                          #include <unistd.h>
                          #include <sys/types.h>
                          
                          void change_directory(const char *path)
                          {
                              if (chdir(path) == -1)
                                  perror(path);
                          }
                          
                          void cd(const char *path)
                          {
                              char cwd[1024];
                              if (getcwd(cwd, sizeof(cwd)) != NULL)
                              {
                                  setenv("OLDPWD", cwd, 1);
                              }
                          
                              if (!path || ft_strcmp(path, "~") != 0)
                                  change_directory(getenv("HOME"));
                              else
                                  change_directory(path);
                          
                              if (getcwd(cwd, sizeof(cwd)) != NULL)
                              {
                                  setenv("PWD", cwd, 1);
                              }
                          }
                          
                          int main()
                          {
                              pid_t pid;
                              pid = fork();
                              if (pid == 0)
                              {
                                  cd("/usr/local");
                                  printf("Child Process: Current directory is %s\n", getenv("PWD"));
                              }
                              else
                              {
                                  printf("Parent Process: Current directory is %s\n", getenv("PWD"));
                              }
                              return 0;
                          }
                          

                          Voici un exemple de code qui utilise une variable d'environnement pour stocker le répertoire courant et le partager entre les processus parent et enfant :

                          #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> void change_directory(const char *path) { if (chdir(path) == -1) perror(path); } void cd(const char *path) { char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) { setenv("OLDPWD", cwd, 1); } if (!path || ft_strcmp(path, "~") != 0) change_directory(getenv("HOME")); else change_directory(path); if (getcwd(cwd, sizeof(cwd)) != NULL) { setenv("PWD", cwd, 1); } } int main() { pid_t pid; pid = fork(); if (pid == 0) { cd("/usr/local"); printf("Child Process: Current directory is %s\n", getenv("PWD")); } else { printf("Parent Process: Current directory is %s\n", getenv("PWD")); } return 0; }

                          Dans cet exemple, nous utilisons la fonction getcwd() pour récupérer le répertoire courant avant de changer de répertoire, et la fonction setenv() pour stocker cette valeur dans les variables d'environnement "OLDPWD" et "PWD". Les processus parent et enfant peuvent ensuite utiliser ces variables pour accéder au répertoire



                          • Partager sur Facebook
                          • Partager sur Twitter
                            22 janvier 2023 à 8:00:46

                            André Parfait a écrit:

                            La fonction chdir() permet de changer le répertoire courant pour le processus en cours d'exécution. Lorsque vous utilisez cette fonction dans un processus enfant créé à l'aide de la fonction fork(), le changement de répertoire ne s'applique qu'au processus enfant et non au processus parent.

                            Lorsque vous utilisez la fonction fork(), un nouveau processus est créé à partir de celui qui l'appelle. Le processus enfant hérite de la plupart des ressources du processus parent, y compris le répertoire courant. C'est pourquoi, lorsque vous utilisez chdir() dans un processus enfant, le répertoire courant ne change pas pour le processus parent.

                            Pour résoudre ce problème, vous pouvez utiliser la fonction chdir() dans le processus parent après avoir créé le processus enfant. Vous pouvez également utiliser une variable d'environnement pour stocker le répertoire courant et la partager entre les processus parent et enfant.

                            Il est important de noter que le changement de répertoire ne modifie pas les variables d'environnements. Il est donc important de récupérer la valeur de PWD pour la stocker dans une variable d'environnement pour pouvoir la réutiliser dans les processus enfants.

                            Voici un exemple de code qui utilise une variable d'environnement pour stocker le répertoire courant et le partager entre les processus parent et enfant :

                            #include <stdio.h>
                            #include <stdlib.h>
                            #include <unistd.h>
                            #include <sys/types.h>
                            
                            void change_directory(const char *path)
                            {
                                if (chdir(path) == -1)
                                    perror(path);
                            }
                            
                            void cd(const char *path)
                            {
                                char cwd[1024];
                                if (getcwd(cwd, sizeof(cwd)) != NULL)
                                {
                                    setenv("OLDPWD", cwd, 1);
                                }
                            
                                if (!path || ft_strcmp(path, "~") != 0)
                                    change_directory(getenv("HOME"));
                                else
                                    change_directory(path);
                            
                                if (getcwd(cwd, sizeof(cwd)) != NULL)
                                {
                                    setenv("PWD", cwd, 1);
                                }
                            }
                            
                            int main()
                            {
                                pid_t pid;
                                pid = fork();
                                if (pid == 0)
                                {
                                    cd("/usr/local");
                                    printf("Child Process: Current directory is %s\n", getenv("PWD"));
                                }
                                else
                                {
                                    printf("Parent Process: Current directory is %s\n", getenv("PWD"));
                                }
                                return 0;
                            }
                            

                            Voici un exemple de code qui utilise une variable d'environnement pour stocker le répertoire courant et le partager entre les processus parent et enfant :

                            #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> void change_directory(const char *path) { if (chdir(path) == -1) perror(path); } void cd(const char *path) { char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) { setenv("OLDPWD", cwd, 1); } if (!path || ft_strcmp(path, "~") != 0) change_directory(getenv("HOME")); else change_directory(path); if (getcwd(cwd, sizeof(cwd)) != NULL) { setenv("PWD", cwd, 1); } } int main() { pid_t pid; pid = fork(); if (pid == 0) { cd("/usr/local"); printf("Child Process: Current directory is %s\n", getenv("PWD")); } else { printf("Parent Process: Current directory is %s\n", getenv("PWD")); } return 0; }

                            Dans cet exemple, nous utilisons la fonction getcwd() pour récupérer le répertoire courant avant de changer de répertoire, et la fonction setenv() pour stocker cette valeur dans les variables d'environnement "OLDPWD" et "PWD". Les processus parent et enfant peuvent ensuite utiliser ces variables pour accéder au répertoire




                            Réponse ChatGPT, à ce qu'il paraît sur stackoverflow c'est une invasion... et les utilisateurs détectés sont sanctionnés, car souvent les réponses ne sont pas correctes.
                            • Partager sur Facebook
                            • Partager sur Twitter

                            Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
                            La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

                              22 janvier 2023 à 9:16:09

                              Au fait, le petit test

                              $ cd /tmp
                              $ cd | cd
                              $ pwd
                              /tmp
                              

                              montre bien que les deux "cd" (qui ramènent au répertoire d'accueil) sont effectués dans des processus à part (*)

                              Pareil pour les affectations aux variables du shell

                              $ a=avant
                              $ echo $a
                              avant
                              $ a=apres | echo $a
                              avant
                              $ echo $a
                              avant
                              
                              


                              (*) ou du moins, en toute rigueur, que ça ne change pas le répertoire courant du shell. Ce qui laisse fortement induire que ce sont des processus différents.

                              -
                              Edité par michelbillaud 22 janvier 2023 à 9:33:04

                              • Partager sur Facebook
                              • Partager sur Twitter
                                5 février 2023 à 21:50:39

                                Incroyable, merci pour les travaux !

                                Je sens que je vais me régaler!

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  23 février 2023 à 8:06:23

                                  Merci Michel pour ce contenu tres interessant, compte-t-il y avoir une maj pour les redirections, les &&, ||, wildcards et subshell ?

                                  Merci bien!

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    23 février 2023 à 9:18:59

                                    sidikiomar a écrit:

                                    Merci Michel pour ce contenu tres interessant, compte-t-il y avoir une maj pour les redirections, les &&, ||, wildcards et subshell ?

                                    Merci bien!


                                    Bonjour,

                                    Over my dead body, parce que

                                    • c'est trop fatigant, ne serait-ce qu'en raison de la syntaxe épouvantable des langages de commande dérivés de sh
                                    • ce qui m'intéresse c'est d'expliquer des petits bouts techniques, le pipeline est là https://www.mbillaud.fr/notes/pipeline.html en insistant sur le raisonnement pour y arriver. (Mon job, c'était d'essayer d'enseigner des choses à des débutants, en décomposant les difficultés, pas de produire du code)
                                    •  développer un truc complet serait fournir une solution aux fainéants qui ne veulent pas faire leur projet de programmation, ce qui ne leur rendra pas service
                                    • si on veut un exemple de shell assez complet, il suffit de chercher un peu, avec tout ce qui est dispo en open-source, ou dans les archives des unix des années 70-80 (sans parler des projets perso présentés fièrement)
                                    • la vie est trop courte
                                    Si j'étais motivé pour développer un interpréteur de commandes
                                    • j'essaierai de définir une syntaxe propre, quitte à ce qu'elle soit très éloignée de sh. La base commune c'est qu'une suite de commandes, une par ligne, soit un script correct
                                    • je ne ferais surtout pas ça en C, qui n'a pas de conteneurs, où le traitement des exceptions est une corvée, etc.
                                    Un bon exercice :
                                    • lire le code d'un shell historique, par exemple https://github.com/seosgithub/sh_unix_v6
                                    • le convertir en C actuel (j'ose pas dire moderne)
                                    • le refactorer (choix des identificateurs, découpage en fonctions etc) sans dire de gros mots
                                    • en déduire les fonctionnalités qu'il implémente.

                                    -
                                    Edité par michelbillaud 23 février 2023 à 9:41:37

                                    • Partager sur Facebook
                                    • Partager sur Twitter

                                    Chdir et fork

                                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                    • Editeur
                                    • Markdown