首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用C编写shell

用C编写shell
EN

Stack Overflow用户
提问于 2013-09-11 05:53:02
回答 2查看 1.7K关注 0票数 0

我正在尝试用C语言编写一个shell,但是遇到了一个问题。shell应该在一个循环中运行,每次都会提示用户读取和解析来自stdin的文本。然后将参数划分为标记,并将每个标记放入参数向量中。然后,代码派生一个子代,然后使用参数向量作为参数运行命令。然后,代码等待该子进程终止,并打印关于该子进程的统计信息(运行时等)。问题是,每当我们运行程序时,当我们输入ls /home (例如)时,它不会列出主目录,而是列出我们当前所在的目录。此外,如果我们试图向代码中添加一个新变量,代码就会停止工作。你知道怎么解决这个问题吗?

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string.h>

#define TRUE 1
#define BUFFERSIZE 129

int main (int argc, char* argv[]){

    int status;
    int who = RUSAGE_CHILDREN;
    struct rusage usage;
    struct rusage before_usage;
    struct timeval start, end;
    while(TRUE){
    printf("==>:  ");
    char input_string[BUFFERSIZE];
    memset(input_string, '\0', BUFFERSIZE);
    fgets(input_string, BUFFERSIZE-1, stdin);

        int i = 0;
        char* input_arguments[32]; //Pointers to what the commands will be
        char* token; //The specific piece of the input "ls" or "/home"
    char* program;
    int run_exec = 1; //To say whether execvp will run


    token = strtok(input_string, " ");
    char* prog = malloc(strlen(token)+1);
    memset(prog, '\0', strlen(token));
    strncpy(prog, token, strlen(token));
    program = prog;
    printf("PROGRAM: %s\n", prog);
    token = strtok(NULL, " ");


    if(strncmp(program, "exit", 4) ==0)
        exit(0);

        while(token != NULL && i < 32){

        printf("TOKEN: %s \n", token);
        char* tmp = malloc(strlen(token)+1);
        memset(tmp, '\0', strlen(token)+1);
        strncpy(tmp, token, strlen(token));
        input_arguments[i]=tmp;
        printf("INPUT: %s \n", input_arguments[i]);

        if(strcmp(program, "cd") == 0){
            chdir(input_arguments[0]);
        run_exec = 0;
        printf("EXEC: %d\n", run_exec);
    }

    printf("%d\n", i);
    i++;
    size_t tmp2 = 50;
    printf("%s\n", getcwd(tmp, tmp2));

    token = strtok(NULL, " ");

    printf("IN LOOP\n");
}
input_arguments[i] = NULL;
printf("AFTER LOOP\n");

if(fork() != 0){
    int start_time = gettimeofday(&start, NULL);
    if(run_exec == 1){
        getrusage(who, &before_usage);
            waitpid(-1, &status, 0);
            int end_time = gettimeofday(&end, NULL);
            double wall_time_passed = (end.tv_sec -start.tv_sec)*1000
                + (end.tv_usec - start.tv_usec)/1000;
            getrusage(who, &usage);
            double user_time = (usage.ru_utime.tv_sec*1000 +
                usage.ru_utime.tv_usec/1000);
            double system_time = (usage.ru_stime.tv_sec*1000 
                + usage.ru_stime.tv_usec/1000);

//long page_faults = 0;
//long soft_faults = 0;
//long invol = 0;
//long vol = 0;
            printf("Number of Page Faults: %ld \n", usage.ru_majflt -before_usage.ru_majflt);
        //printf("soft_faults: %lu\n", soft_faults);
        printf("Number of Page Reclaims: %ld \n",                                usage.ru_minflt - before_usage.ru_minflt);
        //printf("soft_faults: %lu\n", soft_faults);
        printf("Number of times preempted involuntarily: %ld \n",                usage.ru_nivcsw - before_usage.ru_nivcsw);
        printf("Number of times preempted Voluntarily: %ld \n",                     usage.ru_nvcsw - before_usage.ru_nivcsw);
            printf("User Time: %f \n", user_time);
            printf("System Time: %f \n", system_time);
            printf("Wall-Time: %f \n", (wall_time_passed));
    }
}

else{
    if(run_exec == 1){
    printf("RUNNING EXEC: %s, %s \n", program, *input_arguments); 
    printf("EXEC: \n", execvp(program, input_arguments));
    }

}
}

return 0;

}

EN

回答 2

Stack Overflow用户

发布于 2013-09-11 06:08:21

尝试根据以下示例重新格式化您的变量:

代码语言:javascript
复制
....
if(run_exec == 1){
   //printf("RUNNING EXEC: %s, %s \n", program, *input_arguments); 
   //printf("EXEC: \n", execvp(program, input_arguments));
   char* args[] = { "ls", "/home" };
   execvp(args[0], args);
}
.....

您的代码已被注释,并替换为简单的数组"ls“、"/home”

或者,更具体地说,您必须将input_arguments设置为程序名,并开始从input_arguments添加程序参数。例如:

代码语言:javascript
复制
input_arguments[0] = "ls"
input_arguments[1] = "/var/log/"

下面是你的代码的补丁:

代码语言:javascript
复制
27c27
<         int i = 0;
---
>         int i = 1;
38a39
>     input_arguments[0]=prog;
106a108
> 
票数 1
EN

Stack Overflow用户

发布于 2013-09-11 06:41:44

这里有几件事:

  1. 每次你在你的程序中按回车键,它都会跨越一个孩子,而这个孩子最终会等待。是desirable?
  2. char* tmp = malloc(strlen(token)+1);memset(tmp,'\0',strlen(token)+1);strncpy(tmp,token,strlen(token));

  • 你的主要问题是你没有正确地理解execvp man page,因为这就是strlen知道如何停止的方式,因此,上面的memset是不必要的。它声明了以下内容:

当作为该调用的结果执行C语言程序时,应将其作为C语言函数调用进入,如下所示:

int main (int argc,char *argv[]);

后来,它继续说:

arg0表示的参数,...是指向以null结尾的字符串的指针。这些字符串应构成新过程映像可用的参数列表。该列表由null指针终止。参数arg0应指向与某个exec函数通过启动的进程相关联的文件名。

这显然是你错过的要点。argv参数的第一个实参必须是作为path实参传递的相同值(或其允许的变体)。

更重要的是,通常shell可能会探索PATH变量本身,并通过execve调用替换进程映像(最后一个e是因为shell通常允许您修改环境变量),并且它们将在PATH中找到文件的实际路径和用户输入的argv参数作为*path传递。

关于这一点,POSIX标准规定

对严格一致的POSIX应用程序的要求还规定,作为第一个参数传递的值是与正在启动的进程相关联的文件名。..。在某些情况下,传递的文件名不是文件的实际文件名...

所以,如果你有一个像这样的可执行文件:

代码语言:javascript
复制
#include <stdio.h>
int main(int argc, char *argv[]) {
  printf("I was called as %s\n", argv[0]); return 0;
}

您可以将其编译为test,但将其符号链接到another_test

当以test身份运行时,它应该显示I was called as test,但当以another_test身份运行时则不会。这是有道理的,因为程序可能需要知道它们是如何被调用的。例如,busybox需要这样做才能知道用户想要什么。

但你发布的代码不仅仅是添加一些input_arguments[0] = program并在1中启动"int“,我上面放的那些东西也是正确的:不要重复发明轮子,strdup已经在那里了,你应该使用malloc+memset+strncpy而不是strdup。它的三个函数调用更少,代码的作用也更简洁。

每次循环结束时,它都会产生一个子程序,它并不关心程序是否没有产生(可能程序并不存在)。为什么不在检查完run_exec之后再创建分支呢?为什么不等待生成的特定线程(()在父线程上返回的pid_t )?

如果子进程不能生成镜像,为什么不干掉它呢?目前,如果子进程在execvp调用中失败,它将请求更多命令,一旦被杀死,父进程将打印其统计信息并继续执行作业。这可能是意想不到的,而且肯定是违反直觉的。

此外,我不知道你是否注意到了,但是你没有在标准输入中检查EOF,这可能会杀死你,如果你是从文件输入的,而不是交互的(或者如果用户输入了EOF Ctrl.+D)。您可能也应该将退出操作绑定到EOF,否则您将永远无法完成“使用”脚本文件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18729369

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档