Bonjour,
Je m’amuse à faire un shell en C et j’ai vraiment du mal. En particulier j’implémente une redirection : >1
.
Celle-ci fait la chose suivante : elle met le sdout
de la commande dans un fichier et le stderr
de la commande juste en dessous. Par exemple : ls >1 fichier
va créer un fichier qui s’appelle fichier, et mettre au début du fichier le résultat de ls
puis en dessous si il y en a le stderr
de ls (je sépare ces deux affichages par des #).
Voilà ma fonction :
firstRedirection
void firstRedirection (int typeStream, int fd, char* fileName) {
//erasing the file or creating it if it doesn't exist
FILE* testF = fopen(fileName, "ab+");
fclose(testF);
FILE* streamFile = NULL;
streamFile = fopen(fileName, "a+");
char c;
//reading stream and writing in the file
while (read(fd, &c, 1) > 0) {
fputc(c, streamFile);
}
//if stdout then write 80 #
if (typeStream == 1){
fputc('\n', streamFile);
for (int i = 0; i < 80; i++){
fputc('#', streamFile);
}
fputc('\n', streamFile);
}
fclose(streamFile);
}
Voilà la fonction qui l’appelle :
splitStream
void splitStream (char *fileName, int argc, char **argv) {
int stdout[2];
int stderr[2];
//creating the pipes
if (pipe(stdout) == -1 || pipe(stderr) == -1) {
perror("pipe : ");
}
//creating child
int pid = fork();
if (pid < 0) {
perror("fork :");
}
if (pid == 0) { //child
close(stdout[1]);
close(stderr[1]);
firstRedirection(1, stdout[0], "fichier");
firstRedirection(2, stderr[0], "fichier");
}
else { //dad
close(stdout[0]);
close(stderr[0]);
dup2(stdout[1], 1);
dup2(stderr[1], 2);
shell(argc, argv);
}
}
J’ai alors le problème suivant :
- si dans mon main je fais :
char*test[] = {"find", ".", ">1", "fichier", NULL};
splitStream("fichier",4, test);
J’ai exactement le résultat voulu : création du fichier qui s’appelle fichier et dans lequel on trouve le stdout de find .
et en dessous le stderr de find. C’est étrange d’ailleurs non ? J’arrive à récupérer séparément le stdout
et le stderr
avec une seule pipe et un seul processus. Peut-être que la data reste coincé comme je close les pipe, je ne sais trop…
- A l’inverse si je fais tout ça dans une boucle pour faire un shell :
while (1) {
//waiting for user instruction
printf("$> ");
fflush(NULL);
if (!fgets(line, 1024, stdin))
return 0;
//getting rid of the \n character at the end of line
line[strlen(line)-1] = '\0';
char **cmd = parseString(line);
int n = countWords(cmd);
shell(n, cmd);
}
Et que je tape find . >1 fichier
et bien ça mouline dans le vide. Du coup je ne comprends pas du tout. Au début je pensais que le problème venait de ma fonction qui parse un string : elle prend une commande en argument puis la transforme en un array de mot. Mais en faisant des tests ça n’est pas le cas.
De même je ne pense pas que le problème vienne de la fonction shell qui execute la commande. Je met néanmoins ces deux fonctions en cachés au cas-ou.
parseString
char** parseString (char* cmd) {
char ** res = NULL;
char * p = strtok (cmd, " ");
int n_spaces = 0;
while (p) {
res = realloc (res, sizeof (char*) * ++n_spaces);
if (res == NULL){//memory allocation problem
exit (-1);
}
res[n_spaces-1] = p;
p = strtok (NULL, " ");
}
//adding extra last element with value NULL for execvp function
res = realloc (res, sizeof (char*) * (n_spaces+1));
res[n_spaces] = NULL;
return res;
}
shell :
void shell (int argc, char ** argv) {
if (argc > 0) {
if (strcmp(argv[0], "exit") == 0) {
exit(0);
}
else if (strcmp(argv[0], "cd") == 0) {
chdir(argv[2]);
}
else {
pid_t pid = fork();
if (pid == -1) {
printf("\nFailed forking child..");
return;
} else if (pid == 0) {
if (execvp(argv[0], argv) < 0) {
printf("\nCould not execute command..");
return;
}
exit(0);
} else {
// waiting for child to terminate
//int status;
//waitpid(pid, &status, 0);
wait(NULL);
return;
}
}
}
}
EDIT : il semblerait que le problème vienne du while(1)
donc pourquoi le faire un while(1)
interférai avec mon code je ne sais pas…