1

Making a very basic shell given as assignment. Assuming I have a string "ls a b c | grep a"

How can I get something like this

commands[0][0] = "ls"
commands[0][2] = "a"
commands[0][2] = "b"
commands[0][3] = "c"
commands[0][4] = NULL

commands[1][0] = "grep"
commands[1][1] = "a"
commands[1][2] = NULL

First split on "|" and then split further on spaces and hence be able to execute execvp(commands[0][0], commands[0])

I can do single splitting just fine but this double splitting is somewhat troublesome

1
  • This is very closely related to Strange strtok problem. The problem there was splitting first on ; instead of |, but involved double loops of tokenization. The addtional wrinkle there was to preserve the original string unbutchered (since the strtok family of functions modify their input string). One additional observation; once you get to the point of supporting quoted string arguments, any technique based on strtok ceases to be appropriate. However, that is for the future, not an immediate problem. Commented Aug 19, 2012 at 15:25

2 Answers 2

3

I have not tested with execvp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define WHITESPACE 1
#define ARGUMENT 2
#define COMMAND 3
void process_command(const char *string)
{
   char *argument = (char *) 0;
   char *args[100];
   char *const * commands[10];
   int commands_index = 0;
   int char_index = 0;
   int args_index = 0;
   int state = WHITESPACE;
   commands[commands_index] = &args[args_index];
   for (const char *p = string; *p != '\0'; p++)
   {
      if ( *p != ' ' )
      {
         if ( *p == '|' )
         {
            if ( state == ARGUMENT)
            {
               argument[char_index] = '\0';
               char_index = 0;
            }
            state = COMMAND;
            args[args_index] = 0;
            args_index++;
            commands_index++;
            commands[commands_index] = &args[args_index];
         }
         else
         {
            if ( state != ARGUMENT )
            {
               argument = malloc(100);
               args[args_index] = argument;
               args_index++;
               state = ARGUMENT;
            }
            argument[char_index] = *p;
            char_index++;
         }
      }
      else 
      {
         if ( state == ARGUMENT)
         {
            argument[char_index] = '\0';
            char_index = 0;
         }
         state = WHITESPACE;
      }
   }
   argument[char_index] = '\0';
   args[args_index] = 0;
   //execvp(commands[0][0],commands[0]);
   //execvp(commands[1][0],commands[1]);

   for (int i = 0; i <= commands_index; i++)
   {
      int j = 0;
      for (; commands[i][j] != 0;j++)
      {
         printf("commands[%d][%d] = \"%s\"\n",i,j,commands[i][j]);
      }
      printf("commands[%d][%d] = NULL\n",i,j);
   }
   printf("\n");
}



int main(void)
{

   const char *string1 = "ls a b c | grep a";
   const char *string2 = "ls -al|grep txt";
   const char *string3 = "cat file.txt | grep hello |more";
   process_command(string1);
   process_command(string2);
   process_command(string3);
}

output:

commands[0][0] = "ls"
commands[0][1] = "a"
commands[0][2] = "b"
commands[0][3] = "c"
commands[0][4] = NULL
commands[1][0] = "grep"
commands[1][1] = "a"
commands[1][2] = NULL

commands[0][0] = "ls"
commands[0][1] = "-al"
commands[0][2] = NULL
commands[1][0] = "grep"
commands[1][1] = "txt"
commands[1][2] = NULL

commands[0][0] = "cat"
commands[0][1] = "file.txt"
commands[0][2] = NULL
commands[1][0] = "grep"
commands[1][1] = "hello"
commands[1][2] = NULL
commands[2][0] = "more"
commands[2][1] = NULL
Sign up to request clarification or add additional context in comments.

1 Comment

+1: This solution is adaptable to single-quoted string arguments and double-quoted string arguments, and could handle &, ;, || and && command separators as easily as |.
1

You can try using strtok_r to split the string and get tokens and build the expected command line/array of string.

4 Comments

strtok_r accepts argument of type *char where as on the first split I would be requiring **char
@rockstarjindal that is to store position in the string you can pass address of char *. Look at example in the man page of strtok_r
@jacekmigacz my assignment specifically says to take an input, parse fork and execute.
@Rohan I still don't understand how will I pass the results of strtok_r to execvp since the latter accepts a whole array instead of a single subtoken.Will copying work??

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.