我的execvp使用有什么问题?

时间:2015-05-07 18:44:11

标签: c linux exec fgets execvp

我正在编写一个小shell来学习C.现在我想执行自定义命令,但它无法正常工作。

$ ./a.out 
OS>ls
10357: executing ls

failed to execute ls
: (2: No such file or directory)

我不能使用系统调用来执行自定义命令,我应该使用execvp和fork。但为什么现在有效呢?整个代码是

#include<sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
int mystrcmp(char const *, char const *);

struct command
{
    char * const *argv;
};
static _Noreturn void err_syserr(char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
    exit(EXIT_FAILURE);
}
/* Helper function that spawns processes */
static int spawn_proc(int in, int out, struct command *cmd)
{
    pid_t pid;
    if ((pid = fork()) == 0)
    {
        if (in != 0)
        {
            if (dup2(in, 0) < 0)
                err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
            ;
            close(in);
        }
        if (out != 1)
        {
            if (dup2(out, 1) < 0)
                err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
            close(out);
        }
        fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd->argv[0]);
        execvp(cmd->argv[0], cmd->argv);
        err_syserr("failed to execute %s: ", cmd->argv[0]);
    }
    else if (pid < 0)   {
        err_syserr("fork failed: ");
    }
    return pid;
}

/* Helper function that forks pipes */
static void fork_pipes(int n, struct command *cmd)
{
    int i;
    int in = 0;
    int fd[2];
    for (i = 0; i < n - 1; ++i)
    {
        pipe(fd);
        spawn_proc(in, fd[1], cmd + i);
        close(fd[1]);
        in = fd[0];
    }
    if (dup2(in, 0) < 0)    {
        err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
    }
    fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd[i].argv[0]);
    execvp(cmd[i].argv[0], cmd[i].argv);
    err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}

#define BUFFERSIZE 200
int main() {

    char *args[80];
    char buffer[BUFFERSIZE];
    char *prompt = "OS";
    char *a = ">";

    char *tok;
    tok = strtok (buffer," ");


    while(buffer != NULL) {
        bzero(buffer, BUFFERSIZE);
        printf("%s%s",prompt,a);
        fgets(buffer, BUFFERSIZE, stdin);



        if(mystrcmp(buffer,"cd") == 0) {
            tok = strchr(buffer,' ')+1; //use something more powerful
            *strchr(tok, '\n')='\0';
            cd(tok);
        }
        else if(mystrcmp(buffer,"exit") == 0) {
            return 0;
        }
        else {
            //system("ls"); //for testing the CWD/PWD

            char *commandbuffer[] = { buffer, 0 };
            //char *less[] = { "less", 0 };
            struct command cmd[] = { {commandbuffer} };
            fork_pipes(1, cmd);
            printf("Spawned foreground process: %d\n", getpid());
        }
    }
    return 0;
}

int mystrcmp(char const *p, char const *q)
{
    int i = 0;
    for(i = 0; q[i]; i++)
    {
        if(p[i] != q[i])
            return -1;
    }
    return 0;
}

int cd(char *pth) {
    char path[BUFFERSIZE];
    strcpy(path,pth);

    char *token;

    char cwd[BUFFERSIZE];
    if(pth[0] != '/')
    {   // true for the dir in cwd
        getcwd(cwd,sizeof(cwd));
        strcat(cwd,"/");
        strcat(cwd,path);
        chdir(cwd);
    } else { //true for dir w.r.t. /
        chdir(pth);
    }
    printf("Spawned foreground process: %d\n", getpid());
    return 0;
}

3 个答案:

答案 0 :(得分:1)

错误不是因为您使用$state.go('home')而是使用execvpfgets将换行符留在缓冲区中该行的末尾,因此最终您将fgets提供给"ls\n",并且它正确地抱怨它无法找到该命令。

因为我猜你最终还是会替换此代码,目前

execvp

解决问题,直到您正确地进行输入解析。 我不建议使用fgets(buffer, BUFFERSIZE, stdin); strtok(buffer, "\n"); /* quick & dirty: remove newline if there. */ 作为长期解决方案的任何内容。从长远来看,您可能对GNU特定的strtok函数感兴趣,或者确实如此。在libreadline中(如果将您的代码置于GPL下对您来说不是问题)。

答案 1 :(得分:1)

fgets(buffer, BUFFERSIZE, stdin);

buffer总是以&#39; \ n&#39;结尾。因为你以回报结束你的输入。

因此,如果您只是将ls作为命令传递,则程序会获得ls\n,显然PATH中没有此类命令或二进制文件。

要解决此问题,您只需执行以下操作:

fgets(buffer, BUFFERSIZE, stdin);

if (buffer[strlen(buffer)-1] == '\n')
    buffer[strlen(buffer)-1] = '\0';

....

答案 2 :(得分:0)

像往常一样,案件可以用strace来解决。

不幸的是,代码太错了,我写的详尽评论太长了。

  

meh.c:99:13:警告:函数'cd'的隐式声明无效   在C99 [-Wimplicit-function-declaration]               CD(TOK);               ^ meh.c:80:11:警告:未使用的变量'args'[-Wunused-variable]       char * args [80];             ^ meh.c:132:11:警告:未使用的变量'token'[-Wunused-variable]       char *令牌;             ^ 3警告生成。

这是怎么回事?

#include<sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
int mystrcmp(char const *, char const *);

struct command
{
    char * const *argv;
};
static _Noreturn void err_syserr(char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
    exit(EXIT_FAILURE);
}

考虑使用非标准的错误功能。

/* Helper function that spawns processes */
static int spawn_proc(int in, int out, struct command *cmd)
{
    pid_t pid;
    if ((pid = fork()) == 0)
    {
        if (in != 0)
        {
            if (dup2(in, 0) < 0)
                err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
            ;
            close(in);
        }
        if (out != 1)
        {
            if (dup2(out, 1) < 0)
                err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
            close(out);
        }

如果你必须像这样办理登机手续,你可能已经做错了。考虑一下如果'out'为0会发生什么。在这个级别,只要确保你的shell总是打开0,1,2就可以解决这个问题了。

        fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd->argv[0]);
        execvp(cmd->argv[0], cmd->argv);
        err_syserr("failed to execute %s: ", cmd->argv[0]);
    }
    else if (pid < 0)   {
        err_syserr("fork failed: ");
    }

改变可以提前放置父代码并避免长期子代案的缩进。

    return pid;
}

/* Helper function that forks pipes */
static void fork_pipes(int n, struct command *cmd)
{
    int i;
    int in = 0;
    int fd[2];
    for (i = 0; i < n - 1; ++i)
    {
        pipe(fd);
        spawn_proc(in, fd[1], cmd + i);
        close(fd[1]);
        in = fd[0];
    }
    if (dup2(in, 0) < 0)    {
        err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
    }
    fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd[i].argv[0]);

如果带有换行符的printfs不够,则strace会显示问题:

  

execve(“/ usr / bin / ls \ n”,[“ls \ n”],[/ * 58 vars * /])= -1 ENOENT(没有这样的文件或目录)

    execvp(cmd[i].argv[0], cmd[i].argv);

您是否意识到这会覆盖您的shell?

    err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}

#define BUFFERSIZE 200
int main() {

    char *args[80];
    char buffer[BUFFERSIZE];
    char *prompt = "OS";
    char *a = ">";

    char *tok;
    tok = strtok (buffer," ");


    while(buffer != NULL) {
        bzero(buffer, BUFFERSIZE);
        printf("%s%s",prompt,a);
        fgets(buffer, BUFFERSIZE, stdin);



        if(mystrcmp(buffer,"cd") == 0) {
            tok = strchr(buffer,' ')+1; //use something more powerful
            *strchr(tok, '\n')='\0';
            cd(tok);
        }
        else if(mystrcmp(buffer,"exit") == 0) {
            return 0;
        }
        else {
            //system("ls"); //for testing the CWD/PWD

            char *commandbuffer[] = { buffer, 0 };
            //char *less[] = { "less", 0 };
            struct command cmd[] = { {commandbuffer} };
            fork_pipes(1, cmd);
            printf("Spawned foreground process: %d\n", getpid());
        }
    }
    return 0;
}

int mystrcmp(char const *p, char const *q)
{
    int i = 0;

这个初始化有什么用?

    for(i = 0; q[i]; i++)

不正确的。您假设q不长于p。

    {
        if(p[i] != q[i])
            return -1;
    }

有比char-by-char比较更好的方法。

    return 0;
}

这是为了什么呢?

int cd(char *pth) {
    char path[BUFFERSIZE];
    strcpy(path,pth);

路径和pth?人。考虑'orig_path'或其他东西。一个/字的变化/看起来像一个错字,事实上你很容易误输它。 fscking避免。

    char *token;

    char cwd[BUFFERSIZE];
    if(pth[0] != '/')
    {   // true for the dir in cwd
        getcwd(cwd,sizeof(cwd));
        strcat(cwd,"/");
        strcat(cwd,path);
        chdir(cwd);

即使忽略了通常的缓冲区溢出问题和缺少错误检查,这也是不正确的。如果在getcwd之后修改了与此进程相关的目录树,则输入错误的目录(假设chdir成功)。更重要的是,包含'..'的路径对符号链接敏感。

    } else { //true for dir w.r.t. /
        chdir(pth);
    }
    printf("Spawned foreground process: %d\n", getpid());

好像是一个复制贴纸?

    return 0;
}