2015-05-07 106 views
1

我正在写一个小壳来学习C.现在我想执行自定义命令,但它不工作。我的execvp用法有什么问题?

$ ./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; 
} 

回答

1

fgets(buffer, BUFFERSIZE, stdin); 

buffer总是以 '\ n',因为你结束你的输入与一回结束。

因此,如果您只是通过ls作为命令,您的程序将获得ls\n,并且显然在PATH中没有这样的命令或二进制文件。

为了解决这个问题,你可以简单地做到以下几点:

fgets(buffer, BUFFERSIZE, stdin); 

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

.... 
1

的错误不是与您使用的execvp但您使用的fgetsfgets将换行符留在缓冲区中行的末尾,因此最终您将"ls\n"输入execvp,并且它正确地抱怨它找不到该命令。

因为我猜,你最终会取代反正这段代码,就目前而言,

fgets(buffer, BUFFERSIZE, stdin); 
strtok(buffer, "\n"); /* quick & dirty: remove newline if there. */ 

摆脱这个问题,直到你得到周围做输入正确解析。 尽管如此,我不能推荐任何使用strtok作为长期解决方案的东西。从长远来看,您可能对GNU特定的getline函数感兴趣,或者确实在libreadline(如果将您的代码放在GPL下对您来说不是问题)。

0

像往常一样,情况可以用strace的解决。

不幸的是,这段代码太错了,太长时间了,我无法写出详尽的评论。

meh.c:99:13:警告:的功能 'CD' 隐式声明是在C99 [-Wimplicit函数声明] CD(TOK)无效 ; ^ meh.c:80:11:warning:未使用变量'args'[-Wunused-variable] char * args [80]; ^ meh.c:132:11:警告:未使用的变量'令牌'[-Wunused-variable] char * token; ^ 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); 
} 

改为考虑非标准的err函数。

/* 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]); 

如果与newlinewere printfs输出不充分,strace的揭示了问题:

execve的( “/ USR/bin中/ LS \ n”,[ “LS \ n” 个],[/ * 58个瓦尔* /)= -1 ENOENT(没有这样的文件或目录)

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++) 

不正确。你假设q不长于p。

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

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

return 0; 
} 

无论如何这是什么?

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

path and 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成功)。更重要的是,通路,包括“..”是敏感symlinks.1

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

似乎是一个复制帕斯托?

return 0; 
}