Amine Amine - 1 month ago 6
C Question

C Shell to execute commands

I have the following code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

void passe(char *ligne, char **argv)
{
while (*ligne != '\0')
{
while (*ligne == ' ' || *ligne == '\t' || *ligne == '\n')
*ligne++ = '\0';
*argv++ = ligne;
while (*ligne != '\0' && *ligne != ' ' && *ligne != '\t' && *ligne != '\n')
ligne++;
}
*argv = '\0';
}

void executer(char **argv)
{
pid_t pid;
int status;

if ((pid=fork()) == 0)
{
if (execvp(*argv, argv) < 0)
{
printf("*** ERREUR: exec echoué\n");
exit(1);
}
}
wait(&status);
}

int main(void)
{
char ligne[1024];
char *argv[64];

while (1)
{
printf("Shell -> ");
scanf("%s",ligne);
printf("\n");
passe(ligne, argv);
if (strcmp(argv[0], "exit") == 0)
exit(0);
executer(argv);
}
return 0;
}


The programme works fine, however when I parse something like "ps -f" it retunrs "execvp echoué", as the code removes the spaces. What should I exactly do to make it accept comands with options such as ps -f?

Answer

You should use fgets() instead of scanf(). Because fgets does not delete white spaces from your input. scanf function only consumes input until the whitespace. For example if you enter "ps -f" scanf consumes this input as "ps". If you write ps directly to your shell terminal you can face with same exception that is what your programme exception. You can use fgets as stream reader that can be file stream or standard input stream(stdin). I have written code that solves your problem. You can check how you can use.

/* ----------------------------------------------------------------- */
/* PROGRAM  shell.c                                                  */
/*    This program reads in an input line, parses the input line     */
/* into tokens, and use execvp() to execute the command.             */
/* ----------------------------------------------------------------- */

#include  <stdio.h>
#include  <sys/types.h>

/* ----------------------------------------------------------------- */
/* FUNCTION  parse:                                                  */
/*    This function takes an input line and parse it into tokens.    */
/* It first replaces all white spaces with zeros until it hits a     */
/* non-white space character which indicates the beginning of an     */
/* argument.  It saves the address to argv[], and then skips all     */
/* non-white spaces which constitute the argument.                   */
/* ----------------------------------------------------------------- */

void  parse(char *line, char **argv)
{
     while (*line != '\0') {       /* if not the end of line ....... */ 
          while (*line == ' ' || *line == '\t' || *line == '\n')
               *line++ = '\0';     /* replace white spaces with 0    */
          *argv++ = line;          /* save the argument position     */
          while (*line != '\0' && *line != ' ' && 
                 *line != '\t' && *line != '\n') 
               line++;             /* skip the argument until ...    */
     }
     *argv = '\0';                 /* mark the end of argument list  */
}

/* ----------------------------------------------------------------- */
/* FUNCTION execute:                                                 */
/*    This function receives a commend line argument list with the   */
/* first one being a file name followed by its arguments.  Then,     */
/* this function forks a child process to execute the command using  */
/* system call execvp().                                             */
/* ----------------------------------------------------------------- */

void  execute(char **argv)
{
     pid_t  pid;
     int    status;

     if ((pid = fork()) < 0) {     /* fork a child process           */
          printf("*** ERROR: forking child process failed\n");
          exit(1);
     }
     else if (pid == 0) {          /* for the child process:         */
          if (execvp(*argv, argv) < 0) {     /* execute the command  */
               printf("*** ERROR: exec failed\n");
               exit(1);
          }
     }
     else {                                  /* for the parent:      */
          while (wait(&status) != pid)       /* wait for completion  */
               ;
     }
}

/* ----------------------------------------------------------------- */
/*                  The main program starts here                     */
/* ----------------------------------------------------------------- */

void  main(void)
{
     char  line[1024];             /* the input line                 */
     char  *argv[64];              /* the command line argument      */

     while (1) {                   /* repeat until done ....         */
          printf("Shell -> ");     /*   display a prompt             */
          fgets(line,1024,stdin);  /*   read in the command line     */
          int i = strlen(line)-1;
          if( line[ i ] == '\n') 
              line[i] = '\0';
          parse(line, argv);       /*   parse the line               */
          if (strcmp(argv[0], "exit") == 0)  /* is it an "exit"?     */
               exit(0);            /*   exit if it is                */
          execute(argv);           /* otherwise, execute the command */
     }
}

output

Comments