【问题标题】:How can I parse the command line with semicolons?如何用分号解析命令行?
【发布时间】:2020-04-10 20:55:52
【问题描述】:

我正在尝试读取由分号分隔且前后有空格的命令行参数(例如 ls ; date ; cal),但分隔部分一直很困难。当我简单地输入一个单独的命令行(例如lsdate)时,我的代码可以工作,但是每当我输入分号时,它就不起作用(例如ls ; date

这是我的 C 代码:

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);
  }
}

【问题讨论】:

  • 使用strtok() 将输入拆分为一个字符。
  • 请注意,“它不起作用”从不足以说明问题。始终陈述预期的(期望的)行为和观察到的行为,并最好指出它们之间的差异。可能您的问题是 shell 解析命令行,如上面 Jörg Beyer 所述。如果不是显示你的程序做了什么你想让程序做什么
  • @JörgBeyer:程序不通过main 中的argcargv 机制接受参数。你认为shell解析涉及到哪里?
  • splitInput 未按照execvp 的要求以空指针终止。
  • 暂时关闭 execvp 和可能使用 cmets 或 #if 语句的 fork 调用。在很多地方添加printf 语句来告诉你程序正在做什么:它找到了哪些字符串,如果它正在调用它会传递给execvp,等等。熟悉您的程序的实际行为方式。

标签: c command-line separator


【解决方案1】:

有很多方法可以做到这一点。 string.h 提供了几个可以使用的功能,strtok()strsep()strchr(),或strcspn()strspn() 的组合,具体取决于您的需要。您还可以始终在字符串中向下移动指针,从字符串中挑选出想要的标记并忽略空格和多重包含分隔符。从教育的角度来看,以这种方式接近它实际上有很好的指针学习价值。

任何时候你在遍历任何东西时选择不同的部分,而不是尝试包含多个嵌套的while 循环,每个循环都设计为向前扫描以跳过或查找特定类别的字符,通常更有利的是使用 state-loop,您可以在其中使用多个 flags 之一来跟踪不同的状态。 (行字内阅读字符,或字之间阅读分隔符或空格等。)。这样就不需要嵌套循环,您只需使用单个循环来循环每个字符,根据您当前的状态做出相应的响应。

将其用于扫描您的字符串并挑选出以分隔符 ';' 或空格结尾的每个单词,并保留单个状态标志 int in; 以跟踪您是否在字内阅读字符( in = 1;) 或处理空格和分隔符的单词之间 (in = 0;) 并使用 char *sp 作为 start-pointer 指向每个单词的开头,userInput 作为 end -pointer 指向当前正在读取的字符,你可以这样做:

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 */
    }
}

(注意: 上面的delim 字符作为参数与nptrs 一起传递,以传递可用的指针数量,以便在填充指针时保护指针数组边界。另请注意,该函数始终将数组中的下一个指针设置为NULL 作为标记,允许您循环遍历数组中main() 中的指针,直到达到NULL,因为您不返回指针的数量使用,作为函数返回或通过指针参数)

使用';' 或空格作为分隔符解析来自" my; ; ; dog ;;; has;fleas ;" 的单词的简单示例可能是:

#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++);
}

(注意:标头 ctype.h 包含在使用 isspace() 函数来测试空格,而不是将 if() 语句串在一起检查 space'\t''\n'directly。这通常是一种很好的做法。)

使用/输出示例

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

在上面的输出中,所有包含的空格都被删除了。

检查一下,如果您有任何问题,请告诉我。实际上有几十种方法可以处理字符串拆分,这只是一种常见且基本的方法。

【讨论】:

  • 非常感谢。你真的救了我的命。
  • 很高兴它有帮助。有时只需将所有内容放在一起就可以帮助它深入了解。
猜你喜欢
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
  • 2011-04-27
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 2011-05-17
  • 1970-01-01
相关资源
最近更新 更多