blutuu
blutuu

Reputation: 590

Forking with command line arguments

I am building a Linux Shell, and my current headache is passing command line arguments to forked/exec'ed programs and system functions.

Currently all input is tokenized on spaces and new lines, in a global variable char * parsed_arguments. For example, the input dir /usa/folderb would be tokenized as:

parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb

parsed_arguments tokenizes everything perfectly; My issue now is that i wish to only take a subset of parsed_arguments, which excludes the command/ first argument/path to executable to run in the shell, and store them in a new array, called passed_arguments.

so in the previous example dir /usa/folderb

parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb

passed_arguments[0] = /usa/folderb
passed_arguments[1] = etc....

Currently I am not having any luck with this so I'm hoping someone could help me with this. Here is some code of what I have working so far:

How I'm trying to copy arguments:

void  command_Line()
{

  int i = 1;
  for(i;parsed_arguments[i]!=NULL;i++)
    printf("%s",parsed_arguments[i]);

}

Function to read commands:

void readCommand(char newcommand[]){

printf("readCommand: %s\n", newcommand);


//parsed_arguments =  (char* malloc(MAX_ARGS));
//  strcpy(newcommand,inputstring);
  parsed =  parsed_arguments;
  *parsed++ = strtok(newcommand,SEPARATORS);   // tokenize input
    while ((*parsed++ = strtok(NULL,SEPARATORS)))
      //printf("test1\n"); // last entry will be NULL

      //passed_arguments=parsed_arguments[1];

    if(parsed[0]){  
      char *initial_command =parsed[0];

  parsed= parsed_arguments;
  while (*parsed) fprintf(stdout,"%s\n ",*parsed++);
  // free (parsed);
  // free(parsed_arguments);

    }//end of if


  command_Line();

}//end of ReadCommand

Forking function:

else if(strstr(parsed_arguments[0],"./")!=NULL)
    {
      int pid;
      switch(pid=fork()){
      case -1:
       printf("Fork error, aborting\n");
       abort();
      case 0:
        execv(parsed_arguments[0],passed_arguments);

      }

    }

enter image description here

This is what my shell currently outputs. The first time I run it, it outputs something close to what I want, but every subsequent call breaks the program. In addition, each additional call appends the parsed arguments to the output.

This is what the original shell produces. Again it's close to what I want, but not quite. I want to omit the command (i.e. "./testline").

Upvotes: 1

Views: 2033

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753970

Your testline program is a sensible one to have in your toolbox; I have a similar program that I call al (for Argument List) that prints its arguments, one per line. It doesn't print argv[0] though (I know it is called al). You can easily arrange for your testline to skip argv[0] too. Note that Unix convention is that argv[0] is the name of the program; you should not try to change that (you'll be fighting against the entire system).

#include <stdio.h>

int main(int argc, char **argv)
{
    while (*++argv != 0)
        puts(*argv);
    return 0;
}

Your function command_line() is also reasonable except that it relies unnecessarily on global variables. Think of global variables as a nasty smell (H2S, for example); avoid them when you can. It should be more like:

void command_Line(char *argv[])
{
    for (int i = 1; argv[i] != NULL; i++)
        printf("<<%s>>\n", argv[i]);
}

If you're stuck with C89, you'll need to declare int i; outside the loop and use just for (i = 1; ...) in the loop control. Note that the printing here separates each argument on a line on its own, and encloses it in marker characters (<< and >> — change to suit your whims and prejudices). It would be fine to skip the newline in the loop (maybe use a space instead), and then add a newline after the loop (putchar('\n');). This makes a better, more nearly general purpose debug routine. (When I write a 'dump' function, I usually use void dump_argv(FILE *fp, const char *tag, char *argv[]) so that I can print to standard error or standard output, and include a tag string to identify where the dump is written.)

Unfortunately, given the fragmentary nature of your readCommand() function, it is not possible to coherently critique it. The commented out lines are enough to elicit concern, but without the actual code you're running, we can't guess what problems or mistakes you're making. As shown, it is equivalent to:

void readCommand(char newcommand[])
{
    printf("readCommand: %s\n", newcommand);

    parsed = parsed_arguments;
    *parsed++ = strtok(newcommand, SEPARATORS);
    while ((*parsed++ = strtok(NULL, SEPARATORS)) != 0)
    {
        if (parsed[0])
        {
            char *initial_command = parsed[0];
            parsed = parsed_arguments;
            while (*parsed)
                fprintf(stdout, "%s\n ", *parsed++);
        }
    }

    command_Line();
}

The variables parsed and parsed_arguments are both globals and the variable initial_command is set but not used (aka 'pointless'). The if (parsed[0]) test is not safe; you incremented the pointer in the previous line, so it is pointing at indeterminate memory.

Superficially, judging from the screen shots, you are not resetting the parsed_arguments[] and/or passed_arguments[] arrays correctly on the second use; it might be an index that is not being set to zero. Without knowing how the data is allocated, it is hard to know what you might be doing wrong.

I recommend closing this question, going back to your system and producing a minimal SSCCE. It should be under about 100 lines; it need not do the execv() (or fork()), but should print the commands to be executed using a variant of the command_Line() function above. If this answer prevents you deleting (closing) this question, then edit it with your SSCCE code, and notify me with a comment to this answer so I get to see you've done that.

Upvotes: 1

Related Questions