使用 strtok 到 运行 多个命令

Using strtok to run multiple commands

我正在尝试使用 C 创建一个 shell,它可以接受以分号 (;) 分隔的多个命令。目前我正在尝试使用 strtok 来分隔命令,但我认为我没有正确使用它。我将 post 提供所有可能的信息,而无需 post 完整代码。 strtok 使用正确吗?

char *semi=";";
 else
            {
                    char *token=strtok(str,semi);
                    if(token != NULL)
                    {
                            token=strtok(NULL,semi);
                    if((childpid = fork()) == 0)
                    {
                            if ((execvp(args[0], args))<0)//prints error message when unknown command is used
                            {
                                    printf("Error! Command not recognized.\n");
                            }
                            execvp(args[0],args);
                            free(args);//deallocate args
                            exit(0);
                    }

编辑:按照指示,我删除了最初 post 编辑的大部分代码,以仅专注于 strtok 的使用。编译时,临时 shell 将一次接受一个命令。我正在尝试使用“;”同时分隔和 运行 两个命令。我是否正确使用 strtok?如果没有,还有其他选择吗?

如果 strtok() returns NULL,你应该经常检查。我会按如下方式更改结构:

char* semi = ";"; // Your semikolon
char *token = NULL; // Your token string

// ...
// Split first occour of semicolon
token = strtok(str,semi);
if(token == NULL){
    perror("No command given ...");
    return NULL;
}
do {
    // Execute your code here
    // fork() etc.
    // You should get each line (each semikolon seperated string)
    // and it should be stored into token
} while((token = strtok(NULL, semi) != NULL);

我希望,我确实理解你的问题......

但是正如我所见,您需要再次用空格拆分 token 以将它们放入 char-Array 以用于 argv[](第二个参数) 17=]。这里的问题是,strtok() 在内部使用 static (?) 变量来存储最后一个位置。所以在循环中使用另一个 strtok() 会 "destroy" 你的文本。

你可以这样做:

char *str; // Your string ...
char semi[1] = ";"; // Your semikolon AND space; strtok() will split at both
char *token = NULL; // Your token string
int len = 0;
char *token2;
int argvpos = 0;

// ...
// Split first occour of semicolon
token = strtok(str,semi);
if(token == NULL){
    perror("No command given ...");
    return EXIT_FAILURE;
}
do {
    // save length of token
    len = strlen(token);
    // Split for blanks to get the arguments
    token2 = strtok(token," ");
    // Build array of arguments
    while(token2 != NULL){
        args[argvpos++] = token2;
        token2 = strtok(NULL," ");
    }
    // Do something with token (as command)
    // and args (as arguments)
    // ...
} while((token = strtok(token+len+1, semi) != NULL);
// In the while condition you add the length to the token; so you get the "old" last position

我认为这不是一个好的解决方案,但应该可行。我希望,我确实理解你的问题 ;-)

亲切的问候。

为了正常工作,strtok 应该与 while 循环一起使用。此外,您不需要 运行 execvp 两次。

我使用您的代码创建了一个小示例程序来演示如何正确使用您的代码:

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

int main()
{
  char str[] = "ls -1; echo 'hello world'"; // Input commands separated by ';'

  // Break the commands string into an array
  char *commands[10]; // Array to hold a max of 10 commands
  char *semi = ";";
  char *token = strtok(str, semi);
  int i = 0;
  while (token != NULL) 
  {
    commands[i] = token;
    ++i;
    token = strtok(NULL, semi);
  }
  int numCommands = i; // numCommands is the max number of input commands

  // Run each input command in a child process
  i = 0;
  while (i < numCommands)
  {
    printf("Command: %s\n", commands[i]);

    // Tokenize the command so that it can be run using execvp
    char *args[10] = {}; // Array to hold command args
    args[0] = strtok(commands[i], " ");
    int tokenCounter = 0;
    while (args[tokenCounter] != NULL)
    {
      tokenCounter++;
      args[tokenCounter] = strtok(NULL, " ");
    }

    // Create a child process
    int childpid = fork();

    // If this is child process, run the command
    if (childpid == 0)
    {
      if ((execvp(args[0], args)) < 0)
      {
        printf("Error! Command not recognized.\n");
      }
      exit(0);
    }
    // If this is the parent, wait for the child to finish
    else if (childpid > 0)
    {
      wait(&childpid);
    }
    // If the child process could not be created, print an error and exit
    else
    {
      printf("Error: Could not create a child process.\n");
      exit(1);
    }

    ++i;
  }

  return 0;
}