【问题标题】:Writing a simple shell in C using fork/execvp使用 fork/execvp 在 C 中编写一个简单的 shell
【发布时间】:2015-04-14 15:42:43
【问题描述】:

我必须使用系统调用 fork()/execvp() 用 C 语言开发一个简单的 shell。到目前为止,我的代码接受了一个命令,使用 strtok 将其拆分为一个数组 argv,然后我调用 fork 创建一个子进程并执行该命令。我在 ubuntu 中处理这个问题,其中大多数命令都在 /bin/ 目录中,所以我附加程序名称(例如 /bin/ls)并将其用于 execvp 的第一个 arg,然后我给它 argv 数组.如果我输入命令“ls”,我的程序就可以工作,但是当尝试其他命令甚至“ls -l”时,我得到一个“ls:无效选项”。我不确定我在这里做错了什么。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_LEN 1024

int main(){
    char line[BUFFER_LEN];  //get command line
    char* argv[100];        //user command
    char* path= "/bin/";    //set path at bin
    char progpath[20];      //full file path
    int argc;               //arg count

while(1){

    printf("My shell>> ");                    //print shell prompt

        if(!fgets(line, BUFFER_LEN, stdin)){  //get command and put it in line
        break;                                //if user hits CTRL+D break
    }
    if(strcmp(line, "exit\n")==0){            //check if command is exit
        break;
    }

    char *token;                  //split command into separate strings
    token = strtok(line," ");
    int i=0;
    while(token!=NULL){
        argv[i]=token;      
        token = strtok(NULL," ");
        i++;
    }
    argv[i]=NULL;                     //set last value to NULL for execvp

    argc=i;                           //get arg count
    for(i=0; i<argc; i++){
        printf("%s\n", argv[i]);      //print command/args
    }
    strcpy(progpath, path);           //copy /bin/ to file path
    strcat(progpath, argv[0]);            //add program to path

    for(i=0; i<strlen(progpath); i++){    //delete newline
        if(progpath[i]=='\n'){      
            progpath[i]='\0';
        }
    }
    int pid= fork();              //fork child

    if(pid==0){               //Child
        execvp(progpath,argv);
        fprintf(stderr, "Child process could not do execvp\n");

    }else{                    //Parent
        wait(NULL);
        printf("Child exited\n");
    }

}
} 

【问题讨论】:

  • 除非您的 PATH 环境变量不包含 /bin,否则您不需要将 /bin 附加到文件名。另外,这是不安全的,可能与问题无关,但不安全while(token!=NULL) 使其成为while(token!=NULL &amp;&amp; i &lt; 100)

标签: c linux bash shell execvp


【解决方案1】:

无效选项是因为fgets()在你按回车时保留'\n',试试这个

if(!fgets(line, BUFFER_LEN, stdin))
    break;
size_t length = strlen(line);
if (line[length - 1] == '\n')
    line[length - 1] = '\0';

当您尝试调用 ls -l 时,您将 "-l\n" 作为选项传递,因此是消息。

你必须改变

strcmp(line, "exit\n")

strcmp(line, "exit")

【讨论】:

  • 一个很好的地方可以看到在 C 中工作的简单 shell C-Shell
  • 他们使用有害的gets() 来保存标准输入see why,我尝试用fgets()see other methods 替换它,但在ls 命令的情况下我有同样的错误。 ls: cannot access '': No such file or directory
  • 其他一些链接:1.tutorial 2.SO 3.SE
【解决方案2】:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#define BUFFER_LEN 1024

int main(){
    char user_input[BUFFER_LEN];  //get command line
    char* argv[120]; //user command
    int argc ; //argument count
    char* path= "/bin/";  //set path at bin  
    char file_path[50];//full file path

    while(1){
        
        printf("Simple Shell>> ");  // Greeting shell during startup               
        
        if(!fgets(user_input,BUFFER_LEN, stdin)){
            break;  //break if the command length exceed the defined BUFFER_LEN
        }
        
        size_t length = strlen(user_input);

        if(length == 0){
            break;
        }

        if (user_input[length - 1] == '\n'){
            user_input[length - 1] = '\0'; // replace last char by '\0' if it is new line char
        }
        
        //split command using spaces
        char *token;                  
        token = strtok(user_input," ");
        int argc=0;
        if(token == NULL){
            continue;
        }
        while(token!=NULL){
            argv[argc]=token;      
            token = strtok(NULL," ");
            argc++;
        }
        
        argv[argc]=NULL; 
        
        strcpy(file_path, path);  //Assign path to file_path 
        strcat(file_path, argv[0]); //conctanate command and file path           

        if (access(file_path,F_OK)==0){  //check the command is available in /bin
        
            pid_t pid, wpid;
            int status;
            
            pid = fork();
            if (pid == 0) { //child process 
                if (execvp(file_path,argv) == -1) {
                  perror("Child proccess end"); 
                }
                exit(EXIT_FAILURE);
            } 
            else if (pid > 0) { // parent process
                wpid = waitpid(pid, &status, WUNTRACED); 
                while (!WIFEXITED(status) && !WIFSIGNALED(status)){
                    wpid = waitpid(pid, &status, WUNTRACED); 
                }
            } 
            else {
                perror("Fork Failed"); //process id can not be null 
            }
        }
        else {
            printf("Command is not available in the bin\n"); //Command is not available in the bin
        }

    }
} 

【讨论】:

  • 您的 shell 将 path 与每个 token 连接起来,防止输入命令参数。
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-20
相关资源
最近更新 更多