2015-05-07 53 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; 
}