Linux shell 赋值 Execvp 无法执行命令

问题描述

我正在尝试使用 C 为 linux 创建一个简单的 shell。我不明白为什么 execvp() 一直失败。我知道 execvp 不需要与参数一起传递的路径。我尝试通过 strtok 运行我的命令的字符数组来遵循某人的建议。

我不断收到 execvp 失败:没有这样的文件或目录。我只是传递一个简单的“ls”作为我的测试命令。

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

#define MAXARGS         20
#define CMDPROMPT       "theshell>"
#define ARGPROMPT       "Next argument: "
#define prompt(n)       printf("%s",(n) == 0 ? CMDPROMPT : ARGPROMPT);

int main(int argc,char* argv[])
{
        char arg[MAXARGS] = {0};
        int i = 0,should_run = 1,pid,exitstatus;

        while (should_run)
        {
                prompt(*arg);

                fgets(arg,MAXARGS,stdin);                                                                                                                 
                                                                                                                                                            
                char *arg = strtok(arg," ");                                                                                                               
                                                                                                                                                            
                char *arg_tok[MAXARGS];                                                                                                                     
                                                                                                                                                            
                while(arg)                                                                                                                                  
                {                                                                                                                                           
                        arg_tok[i++] = arg;                                                                                                                 
                        arg = strtok(NULL," ");                                                                                                            
                }                                                                                                                                           
                                                                                                                                                            
                pid = fork();                                                                                                                               
                                                                                                                                                            
                switch(pid)
                {
                        case -1:
                                perror("Fork Failed\n");
                                exit(1);
                        case 0:
                                execvp(arg_tok[0],arg_tok);
                                perror("execvp has Failed");
                                exit(1);
                        default:
                                while( wait(&exitstatus) != pid );
                                printf("Child process exited with status %d,%d\n",exitstatus>>8,exitstatus&0377);
                }
        }
}

解决方法

您有许多问题,主要在上面的评论中详细说明。您不使用 execvp() 终止 NULL 指针列表,而是将 argarg 声明为:

    char *arg = strtok(arg," "); 

然后重新声明为:

        char *arg = strtok(arg," "); 

最后,您未能从最后一个指针指向的字符串中删除 '\n'。回想一下,fgets() 读取并包含填充的缓冲区中的 '\n'。因此,只需将 strtok() 的分隔符列表更改为包含 '\n',例如" \n"

总而言之,你可以这样做:

#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>                           /* include sys/wait.h for wait() */

#define MAXARGS         20
#define CMDPROMPT       "theshell> "            /* add space after > */
#define ARGPROMPT       "Next argument: "
#define prompt(n)       printf("%s",(n) == 0 ? CMDPROMPT : ARGPROMPT);

int main (void)
{
    char arg[MAXARGS] = "";
    int i = 0,should_run = 1,pid,exitstatus;

    while (should_run)
    {
        prompt (0);

        fgets (arg,MAXARGS,stdin);

        char *p = strtok (arg," \n");           /* fix shadow of arg,included \n as delim */

        char *arg_tok[MAXARGS] = {NULL};

        while (p) {
            arg_tok[i++] = p;
            p = strtok(NULL," \n");
        }

        pid = fork();

        switch(pid)
        {
            case -1:
                perror("Fork Failed\n");
                exit(1);
            case 0:
                execvp (arg_tok[0],arg_tok);
                perror ("execvp has failed");
                exit(1);
            default:
                while( wait(&exitstatus) != pid );
                printf ("Child process exited with status %d,%d\n",exitstatus>>8,exitstatus&0377);
        }
    }
}

注意:sys/wait.h添加标题wait()

示例使用/输出

$ ./bin/execvp_ls
theshell> ls -al fh
total 152
drwxr-xr-x  2 david david   4096 Jul  1  2018 .
drwxr-xr-x 32 david david 131072 Mar  3 15:16 ..
-rw-r--r--  1 david david    602 Jul  1  2018 Readme_ld_reqd_opts.txt
-rw-r--r--  1 david david    124 Jul  1  2018 conversion.c
-rw-r--r--  1 david david     30 Jul  1  2018 conversion.h
-rw-r--r--  1 david david    250 Jul  1  2018 convert_driver.c
Child process exited with status 0,0
theshell> ^C

还要注意,您对宏的使用虽然很聪明,但通常会导致比解决的问题更多的问题。简单地编写要显示的提示是一种更具可读性的方式。虽然这里的宏使用在这里是微不足道的,但这只会随着代码长度和复杂性的增加而增加。

检查一下,如果您还有其他问题,请告诉我。