简体   繁体   中英

How can I parse the command line with semicolons?

I am trying to read the command line arguments that are separated by a semicolon with a blank space front and back (such as ls; date; cal), but the separation part has been difficult. My codes are working when I simply put an individual command line (such as ls or date ), but whenever I put the semicolon, it does not work (such as ls; date )

Here is my C code:

void parse(char *userInput, char **splitInput)
{
  //read until userInput is not end of line
  while (*userInput != '\0')
  {
    //replace any space in userInput as '\0'
    while (*userInput == ';')
    {
      *userInput++ = '\0';
    }
    //save the argument position
    *splitInput++ = userInput;
    //if userinput is not equal to space, read next userInput
    while (*userInput != ' ' && *userInput != ';' && *userInput != '\0')
    {
      userInput++;
    }
  }
}

void execute(char **splitInput)
{
  pid_t pid = fork();

  if (pid > 0) //parent process
  {
    pid_t parent_pid;
    parent_pid = wait(NULL);
  }
  else if (pid == 0) //child process
  {
    if (execvp(*splitInput, splitInput) < 0) 
    {
      printf("%s: command not found\n", *splitInput);
      exit(1);
    }      
  }  
  else //error
  {
    perror("fort error");
  }
}

void main(void)
{
  char userInput[100]; //execvp's first argument
  char *splitInput[100]; //execvp's second argument

  while(strcmp(userInput,"quit") != 0)
  {
    //ask for a user input
    printf("group 10> ");
    //read the entire line of input
    scanf("%[^\n]", userInput);
    //get characters from input; stop the loop problem
    getchar();
    //quit if user input is equal to quit
    if (strcmp(userInput, "quit") == 0)
    {
      exit(0);
    }
    //parse the input
    parse(userInput, splitInput);
    //execute fork
    execute(splitInput);
  }
}

There are a number of ways to do this. string.h provides several functions that can be used, strtok() , strsep() , strchr() , or a combination of strcspn() and strspn() depending on your needs. You can also always walk-a-pointer down the string picking out the wanted tokens from the string and ignoring whitespace and multiple-included-delimiters. There is actually good pointer learning value in approaching it this way from an education standpoint.

Any time you are looping over anything picking out various pieces, instead of trying to include multiple nested while loops, each designed to scan-forward to either skip or find a certain class of characters, it is often more advantageous to use a state-loop where you use one of more flags to keep track of differing states. (line in-word reading character, or between words reading delimiters or whitespace, etc..). That way no nested loops are required and you simply use a single loop to loop over each character responding accordingly depending on your current state.

Putting that in work to scan down your string and pick out each of the words terminated by a delimiter ';'or by whitespace, and keeping a single state flag int in; to track whether you are in-word reading characters ( in = 1; ) or between words handling spaces and delimiters ( in = 0; ) and use char *sp as the start-pointer pointing to the beginning of each word and userInput as the end-pointer pointing to the current character being read, you could do:

void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
    int in = 0;             /* simple in-word flag 0-false/1-true */
    size_t n = 0;           /* counter to protect splitInput bounds */
    char *sp = userInput;   /* start-pointer initialized to userInput */

    while (n < nptrs - 1) { /* loop while pointers remain unfilled */
        /* if at end, is whitespace or a delimiter */
        if (!*userInput || isspace(*userInput) || *userInput == delim) {
            if (in) {                   /* if in word */
                splitInput[n++] = sp;   /* set pointer to start-pointer */
                splitInput[n] = NULL;   /* set next pointer NULL */
            }
            in = 0;                     /* reset in flag zero */
            if (*userInput)             /* if space or delim, nul-terminate */
                *userInput = 0;
            else    /* otherwise */
                return;                 /* at end-of-string */
        }
        else {  /* normal char */
            if (!in) {                  /* if not in-word */
                sp = userInput;         /* set start-pointer to 1st good char */
                in = 1;                 /* set in-word flag true */
            }
        }
        userInput++;    /* advance to next char */
    }
}

( note: above the delim character is passed as a parameter along with nptrs to pass the number of pointers you have available so you can protect your pointer array bounds while filling pointers. Also note, the function always sets the next pointer in your array to NULL as a sentinel allowing you to loop over the pointers in your array in main() until NULL is reached since you don't return the number of pointers used, either as the function return or through a pointer parameter)

A simple example that parses the words from " my; ; ; dog;;; has;fleas;" using ';' or whitespace as delimiters could be:

#include <stdio.h>
#include <ctype.h>

#define NPTR 32     /* if you need a constant, #define one (or more) */

void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
    int in = 0;             /* simple in-word flag 0-false/1-true */
    size_t n = 0;           /* counter to protect splitInput bounds */
    char *sp = userInput;   /* start-pointer initialized to userInput */

    while (n < nptrs - 1) { /* loop while pointers remain unfilled */
        /* if at end, is whitespace or a delimiter */
        if (!*userInput || isspace(*userInput) || *userInput == delim) {
            if (in) {                   /* if in word */
                splitInput[n++] = sp;   /* set pointer to start-pointer */
                splitInput[n] = NULL;   /* set next pointer NULL */
            }
            in = 0;                     /* reset in flag zero */
            if (*userInput)             /* if space or delim, nul-terminate */
                *userInput = 0;
            else    /* otherwise */
                return;                 /* at end-of-string */
        }
        else {  /* normal char */
            if (!in) {                  /* if not in-word */
                sp = userInput;         /* set start-pointer to 1st good char */
                in = 1;                 /* set in-word flag true */
            }
        }
        userInput++;    /* advance to next char */
    }
}

int main (void) {

    char s[] = "  my; ; ;  dog  ;;; has;fleas  ;", *split[NPTR] = { NULL }, **p = split;

    parse (s, split, ';', NPTR);

    while (*p)
        printf ("'%s'\n", *p++);
}

( note: the header ctype.h is included to make use of the isspace() function to test for whitespace rather than stringing together if() statements checking for space , '\t' or '\n' directly. This is generally good practice.)

Example Use/Output

$ ./bin/split_ptr_arr3
'my'
'dog'
'has'
'fleas'

Note in the output above all included whitespace is removed.

Look things over and let me know if you have questions. There are literally dozens of way to approach splitting strings, this is just one common and basic approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM