【问题标题】:Using strtok to run multiple commands使用 strtok 运行多个命令
【发布时间】:2018-03-17 00:31:28
【问题描述】:

我正在尝试使用 C 创建一个 shell,该 shell 可以接受由分号 (;) 分隔的多个命令。目前我正在尝试使用 strtok 来分隔命令,但我认为我没有正确使用它。我将发布所有我能发布的信息,而不发布整个代码。 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);
                    }

编辑:按照指示,我删除了最初发布的大部分代码,仅专注于 strtok 的使用。编译后的临时外壳将一次接受一个命令。我正在尝试使用“;”分开并同时运行两个命令。我正确使用 strtok 吗?如果没有,是否有替代方案?

【问题讨论】:

  • 请发布一个完整的最小示例 - 可以编译的东西。
  • 并删除所有死代码(cmets 中的代码)。只需向我们展示您当前的代码即可。
  • 我需要共享整个代码才能正常运行。将整个内容发布出来以便您和其他人自己编译会更好吗?我希望只有 strtok 部分就足够了,但我可以发布更多。
  • 把整件事都贴出来是不好的。取而代之的是,在代码周围放置一个小包装,以演示代码的工作原理以及您面临的任何问题。请参阅help center 中的minimal, complete, and verifiable example
  • 我对编码/SO 还是很陌生,从您与我共享的链接来看,它表明要使代码尽可能简单。我不太确定如何在分解时编译代码。仅编辑和突出显示 strtok 的使用会更容易吗?

标签: c shell strtok


【解决方案1】:

您应该经常检查strtok() 是否返回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 再次用空格分开,以将它们放入argv[]argv[](第二个参数)数组中execvp()。这里的问题是,strtok() 在内部使用static (?) 变量来存储最后一个位置。因此,在循环中使用另一个 strtok() 会“破坏”您的文本。

你可以这样做:

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

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

亲切的问候。

【讨论】:

  • 我进入我的辅导中心并让他们帮助/解释。这个答案非常接近我们如何解决我的问题,感谢您付出如此多的努力来提供帮助!
  • 不客气!也许你想接受我的回答? ;-)
【解决方案2】:

为了正常工作,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;
}

【讨论】:

  • 这确实是一个很好的答案+1,我正在谷歌搜索连接管道中标准输入上的多个命令并到达这里。
猜你喜欢
  • 2011-10-20
  • 2020-10-10
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2012-03-13
  • 2014-09-11
相关资源
最近更新 更多