【问题标题】:How can I make a dummy shell take no arguments in C?如何使虚拟外壳在 C 中不带参数?
【发布时间】:2013-12-02 10:29:24
【问题描述】:

我有一个带参数的虚拟 shell 程序。但是,我希望它不带参数,而是提供提示让用户输入可执行程序的名称和参数。

例如:

$dummyshell
>(executable program and parameters go here)

这是我目前的代码:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARRAY_SIZE 1<<16

void parseCmdArgs(char *buffer, char** cmdArgs, 
                size_t cmdArgsSize, size_t *nargs)
{
  char *bufCmdArgs[cmdArgsSize]; 
  char **temp;
  char *buf;
  size_t n, p;

  cmdArgs[0], buf, bufCmdArgs[0] = buffer;  

  for(temp=bufCmdArgs; (*temp=strsep(&buf, " \n\t")) != NULL ;){
    if ((*temp != '\0') && (++temp >= &bufCmdArgs[cmdArgsSize]))
      break;
  }

  for (p=n=0; bufCmdArgs[n]!=NULL; n++){
    if(strlen(bufCmdArgs[n])>0)
      cmdArgs[p++]=bufCmdArgs[n];
  }

  *nargs=p;
  cmdArgs[p]=NULL;
}
  int main(int argc, char *argv[], char *envp[]){
  char buffer[BUFFER_SIZE];
  char *args[ARRAY_SIZE];
  char hflag = 'N';
  int *retStatus;
  size_t nargs;
  pid_t pid;

  while(1){

    printf("$dummyshell ");
    fgets(buffer, BUFFER_SIZE, stdin);
    parseCmdArgs(buffer, args, ARRAY_SIZE, &nargs); 


    if (nargs==0)
      continue;

    if (!strcmp(args[0], "help"))
      {
    printf("cat                    cd (absolute path references only\n");
        printf("exit\n");
        printf("help                   history\n");
        printf("jobs                   kill\n");
        printf("ls                     more\n");
        printf("ps                     pwd\n");
    continue;
      }

    if (!strcmp(args[0], "exit" ))
      exit(0);

    pid = fork();

    if (pid){      
      pid = wait(retStatus);
    }

    else {
      if( execvp(args[0], args)) {
    puts(strerror(errno));
    exit(127);
      }
    }

  }    
  return 0;
}

【问题讨论】:

  • 您认为cmdArgs[0], buf, bufCmdArgs[0] = buffer; 行的作用是什么?它实际上评估未初始化的cmdArgs[0] 并将其丢弃;然后是未初始化的buf 并将其丢弃,最后分配给bufCmdArgs[0]。如果要对所有三个变量赋值,请使用赋值运算符代替逗号运算符:cmdArgs[0] = buf = bufCmdArgs[0] = buffer;。由于您似乎没有使用buf,因此您不妨将其省略(定义和初始化)。
  • 我知道我说错了buf;它被使用。对不起。这就是目测代码的问题。另外,您最终还将意识到命令cdexit(可能还有history)都需要特别注意;它们是 shell 内置的,这是有充分理由的。然而,那是后来的。
  • 谢谢!你知道修改我的程序的方法吗,如果用户在程序及其参数的末尾键入一个“&”符号,虚拟 shell 将在后台而不是前台运行程序?
  • 既没有使用hflag,也没有使用main()的参数;摆脱它们(int main(void))。你应该包括&lt;sys/wait.h&gt;;您应该将retStatus 更改为简单的int,然后将&amp;retStatus 传递给wait()。否则,您需要初始化retStatus,使其指向可以分配给的int。通过这些更改,代码运行正常 - 有点。它正确地执行了ls -l。但是,control-D (EOF) 退出失败了。
  • "cd" 不是你可以(有意义地)执行()的程序。听起来你的 dummyshell 最好写成 shell 函数。

标签: c shell parameters arguments


【解决方案1】:

你觉得这条线有什么作用?

cmdArgs[0], buf, bufCmdArgs[0] = buffer;

它实际上评估未初始化的cmdArgs[0] 并将其丢弃;然后它评估未初始化的buf 并将其丢弃,最后分配给bufCmdArgs[0]。如果要对所有三个变量赋值,请使用赋值运算符代替逗号运算符:

cmdArgs[0] = buf = bufCmdArgs[0] = buffer;

既不使用hflag,也不使用main() 的参数;摆脱他们(int main(void))。你应该#include &lt;sys/wait.h&gt;;您应该将retStatus 更改为简单的int,然后将&amp;retStatus 传递给wait()。否则,您需要初始化retStatus,使其指向可以分配给的int

通过这些更改,代码运行正常 - 有点。它正确地执行了ls -l。但是,control-D (EOF) 退出失败非常可怕(在我能够打断它之前,我看到了很多很简单的目录 ls 列表。

另外,您最终也会意识到命令 cd 和 exit(可能还有 history)都需要特别注意;它们是 shell 内置的,这是有充分理由的。然而,那是后来的。

你知道修改我的程序的方法吗,如果用户在程序及其参数的末尾键入一个“&”符号,虚拟 shell 将在后台而不是前台运行程序?

是的,我确实知道这样做的方法。基本上,您不会执行wait()(当您执行wait() 时,您会检查您收集信息的死进程是否是您预期的进程,或者它是否是您的shell 的后台进程之一。


半工作代码

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFFER_SIZE 1<<16
#define ARRAY_SIZE 1<<16

static void parseCmdArgs(char *buffer, char** cmdArgs, 
                size_t cmdArgsSize, size_t *nargs)
{
    char *bufCmdArgs[cmdArgsSize]; 
    char **temp;
    char *buf;
    size_t n, p;

    cmdArgs[0] = buf = bufCmdArgs[0] = buffer;  

    for(temp=bufCmdArgs; (*temp=strsep(&buf, " \n\t")) != NULL ;){
        if ((*temp != '\0') && (++temp >= &bufCmdArgs[cmdArgsSize]))
            break;
    }

    for (p=n=0; bufCmdArgs[n]!=NULL; n++){
        if(strlen(bufCmdArgs[n])>0)
            cmdArgs[p++]=bufCmdArgs[n];
    }

    *nargs=p;
    cmdArgs[p]=NULL;
}
  //int main(int argc, char *argv[], char *envp[]){
int main(void)
{
    char buffer[BUFFER_SIZE];
    char *args[ARRAY_SIZE];
    int retStatus;
    size_t nargs;
    pid_t pid;

    while(1){

        printf("$dummyshell ");
        fgets(buffer, BUFFER_SIZE, stdin);
        parseCmdArgs(buffer, args, ARRAY_SIZE, &nargs); 


        if (nargs==0)
            continue;

        if (!strcmp(args[0], "help"))
        {
            printf("cat                    cd (absolute path references only\n");
            printf("exit\n");
            printf("help                   history\n");
            printf("jobs                   kill\n");
            printf("ls                     more\n");
            printf("ps                     pwd\n");
            continue;
        }

        if (!strcmp(args[0], "exit" ))
            exit(0);

        pid = fork();

        if (pid){      
            pid = wait(&retStatus);
        }

        else {
            if( execvp(args[0], args)) {
                puts(strerror(errno));
                exit(127);
            }
        }

    }    
    return 0;
}

您可能会注意到错误消息应该报告在标准错误上,而不是标准输出上(所以 puts(strerror(errno)) 应该是 fprintf(stderr, "%s\n", strerror(errno));,因为 fputs() 不添加换行符,但 puts() 会添加)。

【讨论】:

  • 非常感谢,我已经更新了我理解的部分。当键入 control-C 而不是终止程序时,我的程序应该生成一个新的提示行。我该怎么做呢?另外,我不知道如何在区分后台进程和前台进程时使用 wait()。
  • control-C处理也是'信号处理';查找sigaction() 及其亲属。您将需要跟踪您正在等待的子进程的 PID(对于同步命令,最后没有 &amp;),并忽略或报告 wait() 返回的其他进程。是时候再次访问手册了。您与原始代码非常接近。我批评了许多更糟糕的程序。还可以在 SO 上查看其他 DIY shell 问题;有很多。
猜你喜欢
  • 1970-01-01
  • 2013-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
  • 2015-02-19
相关资源
最近更新 更多