execvp()函数总是返回负值

时间:2017-05-16 21:47:12

标签: c system-calls

我正在做一个使用Linux命令函数execvp()练习C的模拟shell程序。我知道每当返回值小于0时,执行execvp中传递的命令时出错。我对我的stringTok()函数进程句子数组正确传入正确的自我打印他们是正面的长度和字符串值用于双重检查。对于句子的最后一个标记,基本上是将参数列表划分为单个标记,我将'\0'字符分配给该标记的最后一个字符。 最终,每个单独的令牌被分配到char*cmdargv[MAX_LINE]中的stringTok()execvp(cmdargv[0], cmdargv)是我传递命令参数的方式。例如," ls -al"是输入,fgets()函数将该字符串读入args[MAX_LINE]。子进程将运行execvp,但返回负值并打印出错误字符串。当我尝试在代码中注释为cmd和argv手动分配值时,这甚至无法正常工作。我想我必须在execvp()中传递错误的值或做错了什么      理想情况下,如果我键入" ls -al",它应该与在linux shell中完全相同,列出当前目录中的所有文件或目录(预期输出)。

输入:
osh> ls -al
输出:
ARGS: len 2
内容:ls
最后的元素115
len 3
内容:-al
最后的元素108
最后一名成员:( null)
osh>错误::坏地址
最后一名成员:( null)
职业安全与卫生>

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

#define MAX_LINE 80 


int  stringTok(char* sentence, char** tokens) {

    const char* delim = " \r\n"; 
    int count = 0; 

    // make copy of sentence for strtok 
    size_t len = strlen(sentence);
    char *copy = malloc(len+1); 
    if(!copy) return 0; // if copy null, return 0 
    strncpy(copy, sentence, len); 

    copy[len] = '\0'; 

    // Allocate and copy tokens 
    for(char* word = strtok(copy, delim); 
        word; 
        word = strtok(NULL, delim)) 
    {   
        size_t len = strlen(word); 
        tokens[count] = malloc(len+1);
        if(!tokens[count]) break; 
            strncpy(tokens[count], word, len); 
            tokens[count][len] = '\0'; 
            count++; 
    } 
        size_t strlenth = strlen(tokens[count - 1]);
    //if(count > 1) {
    //   tokens[count - 1][strlenth -1] = '\0'; 
    //}
    tokens[count] = NULL; 
    return count;   
}

int main(void) {
    char args[MAX_LINE]; // command to be executed
    char *cmdargv[MAX_LINE]; // command tokens

    int should_run = 1; 

    do {
        printf("osh>");
        fflush(stdout); 
        fgets(args, MAX_LINE, stdin);
        //fflush(stdout); 

        printf("args:<%s>\n", args);

        int count = stringTok(args, cmdargv);
        for(int i = 0; i < count; i++) {
            printf("len %ld\n", strlen(cmdargv[i]));
            printf("content: %s\n", cmdargv[i]);
            printf("last elememnt %d\n", cmdargv[i][strlen(cmdargv[i]) - 1]);
        }
        cmdargv[count] = NULL;      
            printf("last member:%s", cmdargv[count]);   
        //printf("cmdargv:%s\n", cmdargv[0]);
        //printf("cmdargc:%d\n", cmdargv[0][strlen(cmdargv[0]) - 1]);
        int pid = fork(); 

        char *cmd = "ls";
        char *argv[3];
        argv[0] = "ls";
        argv[1] = "-la";    

        if(pid == 0) {
            execvp(cmd, argv); 
            if(execvp(cmd, argv) < 0) {
              //printf("Error Executing Command\n");
              perror("Error:");
            }
        } else if (pid > 0) { 
             // if '&' {
             // wait(&pid)
             //}    
        } else {
            printf("Fork failed\n");
            exit(1); 
        } 
        printf("\n"); 
    } while(should_run); 
    return 0;
}

2 个答案:

答案 0 :(得分:2)

  

execv()execvp()execvpe()函数提供了一个数组          指向以null结尾的字符串的指针,表示参数列表          可用于新计划。第一个论点,按照惯例,          应指向与正在执行的文件关联的文件名。          指针数组必须由空指针

终止

由于数组只是作为C中的指针传递,并且调用函数没有隐藏的方法来知道数组的长度,因此必须明确给出它。在单独的参数中(例如,sizefgets()的参数),或者以已知值结尾的数组,如NULL(与execv*一样)。您的代码没有将最终参数设置为NULL,这可能导致execlp返回错误地址错误或类似错误,因为您设置的未初始化值将被视为指针并且很可能指向无效的内存地址。

修复该问题会使exec在我这里工作。

此外,您应该使用perror()之类的内容而不是常量错误消息来获取有关发生的错误的一些信息。如果exec失败,子进程将继续运行,因此使用_exit()终止它。

答案 1 :(得分:1)

以下是适用于我的代码版本。它在内存分配上有点清晰 - 你在父shell中疯狂泄漏(命令行的副本和每个参数的单独副本)。他们很容易清理。此代码正确处理EOF,而不是运行到无限循环。它会打印命令信息而不会崩溃。

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

#define MAX_LINE 80

static int stringTok(char *sentence, char **tokens)
{
    const char *delim = " ";
    int count = 0;

    size_t len = strlen(sentence);
    char *copy = malloc(len + 1);
    if (!copy)
        return 0;
    strncpy(copy, sentence, len);

    copy[len] = '\0';

    for (char *word = strtok(copy, delim);
         word;
         word = strtok(NULL, delim))
    {
        size_t len = strlen(word);
        tokens[count] = malloc(len + 1);
        if (!tokens[count])
            break;
        strncpy(tokens[count], word, len);
        tokens[count][len] = '\0';
        count++;
    }
    size_t length = strlen(tokens[count - 1]);
    tokens[count - 1][length - 1] = '\0';
    tokens[count] = 0;
    free(copy);
    return count;
}

int main(void)
{
    char args[MAX_LINE]; // command to be executed
    char *cmdargv[MAX_LINE]; // command tokens

    int should_run = 1;

    do
    {
        printf("osh>");
        fflush(stdout);
        if (fgets(args, MAX_LINE, stdin) == 0)
        {
            putchar('\n');
            break;
        }

        printf("args:%s\n", args);

        int count = stringTok(args, cmdargv);
        for (int i = 0; i < count; i++)
        {
            printf("len %ld\n", strlen(cmdargv[i]));
            printf("content: %s\n", cmdargv[i]);
            printf("last character %d\n", cmdargv[i][strlen(cmdargv[i]) - 1]);
        }

        int pid = fork();

        if (pid == 0)
        {
            for (int i = 0; cmdargv[i] != 0; i++)
                fprintf(stderr, "%d: [%s]\n", i, cmdargv[i]);
            execvp(cmdargv[0], cmdargv);
            int errnum = errno;
            fprintf(stderr, "Error executing command %s (%d: %s)\n",
                    cmdargv[0], errnum, strerror(errnum));
            exit(1);
        }
        else if (pid < 0)
        {
            fprintf(stderr, "Fork failed\n");
            exit(1);
        }
        else
        {
            for (int i = 0; cmdargv[i] != 0; i++)
                free(cmdargv[i]);
            int corpse;
            int status;
            while ((corpse = waitpid(0, &status, 0)) > 0)
                fprintf(stderr, "PID %d exited with status 0x%.4X\n", corpse, status);
            printf("\n");
        }
    } while (should_run);
    return 0;
}

样品运行

我的程序是可执行文件xv97(来源xv97.c):

$ make xv97
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition xv97.c -o xv97
$ ./xv97
osh> extraordinary gallantry
args: extraordinary gallantry

len 13
content: extraordinary
last character 121
len 9
content: gallantry
last character 121
0: [extraordinary]
1: [gallantry]
Error executing command extraordinary (2: No such file or directory)
PID 17445 exited with status 0x0100

osh>    ls    -l    .       
args:    ls    -l    .

len 2
content: ls
last character 115
len 2
content: -l
last character 108
len 1
content: .
last character 46
0: [ls]
1: [-l]
2: [.]
total 8488
-rw-r--r--    1 jleffler  staff     2223 Feb 14 13:40 README.md
drwxr-xr-x   19 jleffler  staff      646 May 12 10:15 bin
-rwxr-xr-x    1 jleffler  staff     8712 May 16 16:02 classifier
-rw-r--r--    1 jleffler  staff      637 May  8 08:21 classifier.c
drwxr-xr-x    3 jleffler  staff      102 May  1 10:23 classifier.dSYM
drwxr-xr-x   10 jleffler  staff      340 May  1 13:35 doc
drwxr-xr-x    7 jleffler  staff      238 Feb 23 15:11 etc
drwxr-xr-x   18 jleffler  staff      612 Apr  4 12:14 inc
-rwxr-xr-x    1 jleffler  staff     8992 May 16 16:02 iq89
-rw-r--r--    1 jleffler  staff     2464 May  1 20:43 iq89.c
drwxr-xr-x    3 jleffler  staff      102 Apr 20 11:56 iq89.dSYM
drwxr-xr-x    5 jleffler  staff      170 Feb 14 13:40 lib
-rw-r--r--    1 jleffler  staff      390 May  1 13:35 makefile
drwxr-xr-x  168 jleffler  staff     5712 May 12 11:56 src
-rwxr-xr-x    1 jleffler  staff     9540 May 16 16:43 xv97
-rw-r--r--    1 jleffler  staff     2432 May 16 16:43 xv97.c
drwxr-xr-x    3 jleffler  staff      102 May 16 15:55 xv97.dSYM
PID 17452 exited with status 0x0000

osh>^D
$

(我编辑了一些文件列表 - 这就是为什么尺寸与显示的文件大小相差无几的原因。)

我们可以讨论为什么最多一行每天只有80个字符。

我在运行macOS Sierra 10.12.5的Mac上进行测试,使用GCC 7.1.0作为编译器。