如何在C中创建一个虚拟shell不带参数?

时间:2013-11-19 02:21:30

标签: c shell parameters arguments

我有一个带参数的虚拟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;
}

1 个答案:

答案 0 :(得分:0)

您认为这条线有什么作用?

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

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

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

hflagmain()的参数均未使用;摆脱他们(int main(void))。你应该#include <sys/wait.h>;您应该将retStatus更改为简单的int,然后将&retStatus传递给wait()。如果不这样做,您需要初始化retStatus,使其指向可以分配到的int

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

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

  

您知道如何修改我的程序,以便用户输入'&amp;'程序结束时的符号及其参数,虚拟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()确实)。