【问题标题】:Simple shell with fork and exec带有 fork 和 exec 的简单 shell
【发布时间】:2018-04-03 07:25:41
【问题描述】:

我正在尝试制作一个简单的 shell,运行来自 PATH 的任何命令,比如说 ls 或 pwd du gedit 等。我在执行 exec 时遇到了问题。我要求如果我进入空间没有任何反应,并且如果我键入 exit,它就会终止。任何帮助表示赞赏

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

#define BUFFER 1024

int main() {
    char line[BUFFER];
    char* args[100];
    char* path = "";
    char program[20];


while(1){
    printf("$ ");
         if(!fgets(line, BUFFER, stdin))
          break;
          size_t length = strlen(line);
         if (line[length - 1] == '\n')
         line[length - 1] = '\0';
     if(strcmp(line, "exit")==0)  break;
         strcpy(program,path);           
         strcat(program,line);
    int pid= fork();              //fork child

        if(pid==0){               //Child
        execlp(program,line,(char *)NULL);    
    }else{                    //Parent
        wait(NULL);
}
}
}   

【问题讨论】:

    标签: c shell fork exec implementation


    【解决方案1】:
    1. 您有 2 个fgets() 电话。删除第一个fgets(line, BUFFER, stdin);

    2. fgets() 将在缓冲区中有空间时读取换行符。您需要删除它,因为当您输入exit 时,您实际上会输入exit\n,并且没有/bin/exit\n 的命令。

    以下代码演示了删除换行符:

        if(!fgets(line, BUFFER, stdin))
            break;
        char *p = strchr(line, '\n');
        if (p) *p = 0;
    
    1. 您的用法有误。查看execl的手册。您需要传递参数:execl(program, line, (char *)NULL); 注意NULL 的最后一个参数的转换。如果NULL 被定义为0,那么转换就变得很有必要了,因为execl 是一个可变参数函数。

    使用execvp的修改示例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <string.h>
    
    #define BUFFER 1024
    
    int main(void) {
        char line[BUFFER];
    
        while(1) {
            printf("$ ");
            if(!fgets(line, BUFFER, stdin)) break;
            char *p = strchr(line, '\n');
            if (p) *p = 0;
            if(strcmp(line, "exit")==0) break;
            char *args[] = {line, (char*)0};
            int pid= fork();              //fork child
            if(pid==0) {               //Child
                execvp(line, args);
                perror("exec");
                exit(1);
            } else {                    //Parent
                wait(NULL);
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 谢谢你成功了。但我刚刚看到我需要执行 $PATH 目录中的任何程序。怎么能做到这一点?我也想退出,即使我输入 exit(之后的任何空格)
    • 然后使用 exec 的“路径”变体(execlpexecvpexecvpe)将在 PATH 中搜索命令。我已经更新了显示如何使用strchr() 删除换行符的答案
    • 我使用了 execlp 但 du(disk usage) 命令或 gedit 不起作用。如果我没记错的话,那是在 usr/bin 上,而我在代码开头的路径变量不包括它吗?
    • 是的,如果您使用路径变体,则不要在前面加上 /bin/(就像您在代码中所做的那样)。您可以使用 shell 命令which 找到实际路径。例如,运行which gedit,它会为您提供位置。
    • 你能帮我编码吗?我做错了。我是否在开始时删除垃圾箱?我也不能在程序中使用哪个?我需要执行 usr/bin 或 /bin/。提前致谢
    【解决方案2】:

    您好,请参阅下面的更正以及我的评论。

    int main() {
    char line[BUFFER];
    char* args[100];
    char* path = "/bin/";
    char program[20];
    char command[50];
    
    while(1){
        printf("$ ");
        if(!fgets(line, BUFFER, stdin))
            break;
        memset(command,0,sizeof(command));
        if(strncmp(line, "exit", (strlen(line)-1))==0)  break;
        strncpy(command,line,(strlen(line)-1));
        strcpy(program, path);
        strcat(program,command);
        int pid= fork();              //fork child
        if(pid==0){               //Child
            execl(program,command,NULL);
            exit(0);// you must exit from the child because now you are inside while loop of child. Otherwise you have to type exit twice to exit from the application. Because your while loop also became the part of every child and from the child again it will call fork and create a child again
        }else{
            wait(NULL);
        }
     }
    }
    

    还要支持所有命令执行,请参阅函数execl,您需要如何在其中传递参数。因此,您需要拆分命令并为execl 正确创建参数列表。

    【讨论】:

    • 我怎样才能让它运行 $PATH 中的任何程序(也可以说是 usr/bin),而不仅仅是 /bin/。我编辑了我的原始帖子。
    猜你喜欢
    • 2015-12-01
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多