bonjour a tous,
je suis actuellement en train de réaliser un mini shell mais je rencontre un petit problème pour appeler des commande de base de linux comme par exemple ls...
malheureusement je n'est pas le droit d'utiliser l'appel système execlp...
je suis obligé d'utilisé getenv() et execl
je vous mets le code que j'ai réalisé:
char buffer[4016];
//affiche le prompt ">"
void affiche_invite()
{
printf("> ");
//
fflush(stdout);
}
//permet de lire une commande saisie au clavier
void lecture_commande()
{
int fd;
//récupère la saisie clavier
if (!fgets(buffer,sizeof(buffer)-1,stdin)) {
/* ^D ou fin de fichier => on quittte */
printf("\n");
exit(0);
}
}
void execute_commande()
{
char *path;
path=getenv("PATH");
//fork pour lancement de commande en arriere plan
int pid,fd,status;
pid=fork();
if (pid < 0) {
fprintf(stderr,"Erreur Fork()\n" );
}
if (pid==0) {
/* fils */
if(fd=(execl(path,buffer,NULL)==0)){
perror("erreur execl");
exit(0);
}else{
if(pid>0){
waitpid(pid,&status,0);
}
}
}
}
int main()
{
//assignation mavar=12(a faire)
//permet de garder le prompt apres saisie de commande
while(1){
//lance les fonction
affiche_invite();
lecture_commande();
execute_commande();
}
return(0);
}
mon problème se situe dans la fonction execute_commande
est que quelqu'un pourrai éventuellement m'aider a résoudre mon problème s'il vous plait
merci d'avance
salut,
c'est ici que ça coince: execl(path,buffer,NULL)
En fait la page de manuel de la fonction execl est pas super limpide mais voila ce qu'il faut comprendre :
le premier argument n'est pas le PATH (variable d'environement) mais le path complet vers le programme que tu souhaite executer, c'est a dire "/bin/ls" si tu veux executer ls. le second argument est identique. J'ai pas bien compris pourquoi mais c'est ce que dis la doc : The first argument, by convention, should point to the file name associated with the file being executed.
les arguments suivant sont les paramètres éventuels que tu voudrais passé a ton programme et le dernier paramètre est NULL comme tu l'avais mis.
Donc en gros pour appeler ls il faut faire comme ceci :
execl("bin/ls","/bin/ls",NULL);
maintenant la partie un peu compliquée qu'il te reste a faire c'est éplucher les dossier du PATH (variable system cette fois ci) pour trouver le chemin complet vers la commande que ton utilisateur a tapée. C'est a dire transformer comme ceci :
"ls" -> "/bin/ls"
"file" -> "/usr/bin/file"
...
Bonne chance
apres relecture je me rend compte qu'il y a un passage moyennement clair :
le second argument est identique. J'ai pas bien compris
pourquoi mais c'est ce que dis la doc : The first argument, by
convention, should point to the file name associated with the
file being executed.
dans la citation de la page de man "the first argument" fait référence au premier argument passé du programme que tu veux executer, c'est a dire le second paramètre passé a execl.
par exemple pour executer "echo toto" :
execl("/bin/echo","/bin/echo", "toto", NULL)
seul toto est effectivement passé en paramètre à /bin/echo
J'ai pas tout lu, mais :
execl("/bin/echo","/bin/echo", "toto", NULL)
à mon avis, le deuxième "/bin/echo" est aussi passé en argument à /bin/echo (le premier). C'est probablement l'éternel histoire de argv[0] qui contient le nom du programme, non ?
arf zut j'avais zappé argv[0] en effet. Bon bah maintenant ça parrait logique effectivement.
merci pour vaut réponses.
Donc en gros ce qu'il faut que je fasse c'est d'ajouter la commande voulu derrière mon path ( a cause de mon path=getenv("PATH"))
exemple:
dans mon mini shell, si un utilisateur tape rm
quand je récupère la variable d'environnement (qui contient plusieurs chemin) rm doit se mettre à la fin (/bin/rm) pour pouvoir utiliser mon execl.
je sais pas si j'ai été clair.
oui c'est ça l'idée.
la difficulté c'est de trouver dans lequel des dossiers de ton PATH se trouve la commande en question.
par exemple, sur ma machine, si je fais
echo $PATH
j'obtiens une suite de chemins vers des dossiers qui contiennent les programmes accessibles depuis mon shell
/opt/local/bin:/opt/local/sbin:/usr/local/Trolltec
h/Qt-4.3.2/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/
local/bin:/usr/local/git/bin:/usr/texbin:/usr/X11/
bin:/opt/local/bin:/opt/local/sbin:./:/usr/local/T
rolltech/Qt-4.3.2/bin
le seul moyen de savoir dans lequel de ces dossier se trouve le programme à lancer est de tous les parcourir à la recherche du programme en question. Par exemple ls et cp se trouvent dans "/bin" mais file et vim se trouvent dans "/usr/bin".
Donc tu peux pas directement coller le nom de ton programme à la fin de ton PATH, c'est un peu plus subtile que ça.
Ok merci pour la réponse. Maintenant je vois pourquoi je n'ai pas le droit d'utilisé execlp.
J'ai du travail lol surtout que je vois pas comment je faire...
Une autre question: est-il possible de spliter le contenu que renvois le PATH. Je m'explique le PATH renvoit :
/opt/local/bin:/opt/local/sbin:/usr/local/Trolltec
h/Qt-4.3.2/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/
local/bin:/usr/local/git/bin:/usr/texbin:/usr/X11/
bin:/opt/local/bin:/opt/local/sbin:./:/usr/local/T
rolltech/Qt-4.3.2/bin (en prenant l'exemple de Guyver2)
et ensuite de faire en sorte d'avoir /opt/local/bin/ puis /opt/local/sbin/ etc... pour pourvoir ensuite de demander à l'ordinateur si file /opt/local/bin/ ne se trouve pas dans une variable va voir dans une autre.
Autre petite chose lol, est ce quelqu'un pourrai m'indiquer comment je pourrai voir le code source de la commande execlp s'il vous plait pour que je m'en inspire un peu si j'arrive a comprendre son code ^^
Merci d'avance.
pour le découpage du PATH tu peux utiliser "strchr" qui te donne la position dans une chaine d'un caractère.
Avec ça tu peux découper ton PATH en extrayant un chemin a la fois.
pour le code de execlp
http://www.google.fr/codesearch/p?hl=fr#5ge3gHPB4K4/gnu/glibc/glibc-2.5.tar.bz2|HT3Jwvgod1I/glibc-2.5/posix/execlp.c&q=execlp
regarde sur le coté tu as les autres aussi.
Merci de ton coup de main ^^. dit moi tu connais pleins de truc j'ai l'impression en C lol.
en tout cas merci pour ton aide les autres aussi. Si j'ai un petit problème je reviendrai vous voir et si mon code fonctionne je le posterai (pi être que ca aidera d'autre personne)
une question,est que l'on peut utilisé execl de cette façon:
execl("/usr/bin/echo,/bin/echo","/usr/bin/echo,/bi
n/echo", "toto", NULL)
en gros si il ne trouve echo dans /usr/bin/echo il va dans /bin/echo?
ou il faut obligatoirement un seul chemin qui obligatoirement le bon (exemple de Chris_27)
execl("/bin/echo","/bin/echo", "toto", NULL)
oui la seule version valide c'est celle avec un seul chemin. Dommage ![]()
Oki lol je m'en doutais un peu mais qui ne tente rien n'a rien comme on dit lol
Merci encore Guyver2
Re bonjour c'est encore moi apres presque un mois...
Voila c'est encore pour le même code...
Cette je sens que je suis vraiment proche de mon but mais j'ai un soucis lorsque je lance mon code et que je tape une commande par exemple ls ou pwd rien ne se passe...
pourtant lorsque que je fais des test pour lancer mon code en utilisant juste la fonction recherche_commande et recup_chemin les commandes s'execute...
Je ne vois pas d'ou proviens le problème est ce que quelqu'un pourrai m'aider?
merci d'avance
ps: voici mon code
char ligne;
//affiche le prompt ">"
void affiche_invite()
{
printf("> ");
fflush(stdout);
}
//permet de lire une commande saisie au clavier
void lecture_commande()
{
int fd;
//récupère la saisie clavier
if (!fgets(ligne,sizeof(ligne)-1,stdin)){
printf("\n");
exit(0);
}
}
//recherche et execution de commande
void recherche_commande(char* repertoire)
{
//"fd" de type entier
int fd;
//"rep" de type repertoire
DIR *rep;
//"entrer" de type dirent initialise a NULL
struct dirent *entrer=NULL;
//Ouverture du "repertoire"
rep=opendir(repertoire);
//Tant qu'il est possible de parcourir le repertoire
while((entrer=readdir(rep))!=NULL){
//si on se trouve dans le repertoire courant et que le contenu est égale au contenu de la variable ligne
if(strcmp(entrer->d_name,ligne)==1){
//allocation de memoire pour buffer ; incrementation par pas
char *buffer=malloc(strlen(repertoire)+1 +strlen(entrer->d_name)+1);
//copie le nom du repertoire vers le pointeur buffer
strcpy(buffer, repertoire);
//concatenation du buffer avec slash a la fin
strcat(buffer,"/");
//concatenation du buffer avec le nom du fichier (fichier regulier ou repertoire)
strcat(buffer,ligne);
//exécution de la commande passer par la variable ligne
execl(buffer,ligne,NULL);
//libération de la memoire
free(buffer);
}
}
}
//recupere le chemin
void recup_chemin()
{
char* path = getenv("PATH");
char subPath[50];
int i = 0, j = 0;
for(i = 0;i < strlen(path);i++){
if(path[i] == ':'){
subPath[j] = '\0'; // On rajoute un caractere NULL pour marquer la fin de la chaine
j = 0;
//execution de la fonction recherche_commande
recherche_commande(subPath);
}else{
subPath[j] = path[i];
j++;
}
}
}
void execute_commande()
{
//fork pour lancement des commandes
int pid,fd,status;
//si erreur fork
pid=fork();
if (pid < 0){
fprintf(stderr,"Erreur Fork()\n" );
}
if (pid==0){
//fils
//execution de la fonction recup_chemin
recup_chemin();
}else{
//pere
if(pid>0){
//on attend si ce n'est pas terminé
waitpid(pid,&status,0);
}
}
}
int main(int argc, char *argv[])
{
//assignation mavar=12(a faire)
//permet de garder le prompt apres saisie de commande
while(1){
//lance les fonction
affiche_invite();
lecture_commande();
execute_commande();
}
return(0);
}
Je n'ai pas bien compris pourquoi tu parcours des repertoires. La recherche dans PATH est fait automatiquement par execve(2) si ma memoire est bonne.
Merci de ta réponse godrik.
Je sais bien mais le problème c'est que je n'est pas le droit d'utiliser les dérivés de execl...d'où mon parcourt... D'ailleurs il me semble pour mon cas si je pouvais utiliser les dérivé de excel il aurait fallu utiliser execlp et pas execlve si je ne me trompe pas.
D'accord, c'est un genre d'exercice de cours. Je reposte ton code avec indentation et coloration syntaxique. Je n'ai pas le temps de regarer maintenant, mais j'aurais peut etre du temps demain.
http://pastebin.com/hCHBTafz
bonjour ^^,
voila, j'ai réussi a faire mon mini shell depuis quelque jour déjà mais je n'est pas penser à le mettre sur le forum... désolé.
maintenant j'ai une question c'est plus pour moi en fait (il y en a pas besoin pour mon shell)
Les commandes du type cd son implémenter directement dans linux a l'inverse des commande comme ls par exemple qui son des fichiers exécutable.
sur le net pour les site que j'ai pu voir, toutes les personnes désirant faire un cd, code le cd entièrement avec l'appel système chdir... mais ca ne serai pas plus simple de dire aux mini shell d'exécuter le /bin/bash? car si on fait echo $BASH il nous retourne cette valeur... et d'après ce que je comprends c'est lui qui exécute les commandes du type cd, etc...
ok, il faut comprendre comment fonctionne cd. cd change le repertoire courant avec un appel systema chdir. Le repertoire courant est une notion qui est definie par processus. Un processus peut savoir dans quel repertoire il est en appelant getcwd.
A la creation d'un processus, le repertoire courant est celui du processus parent.
Si tu appelle si tu execute /bin/bash et fait cd dans bash, alors tu vas changer le repertoire courant du processus bash mais pas de ton shell.
Donc les programme que tu lancera ulterieurement depuis ton shell n'auront pas le bon repertoire courant. C'est pour cela qu'il faut appeller chdir a l'interieur de ton shell. On implemente generalement cela en ajoutant une commande speciale dans ton shell ( que l'on appelle generalement built-in ) pour faire se changement de repertoire courant.
a ok je comprends mieux.
merci beaucoup