2016-02-12 66 views
1

我一直在這個項目上工作了一段時間。目的是製作一個可以完成所有shell命令(除cd之外)的正常運行的shell。它幾乎做了我希望它做的所有事情,除了一些事情。首先,當我將'&'表示後臺處理時,它會執行此操作,但不會打印另一個myshell>行。我仍然可以輸入內容,但是myshell>永遠不會顯示,無論我在哪裏放置另一個cout < <「myshell>」;。Myshell分段錯誤,獲取輸入可能存在問題?

另一個問題是,如果我按下回車鍵,使得myString爲空,很多次,它會以seg故障使程序崩潰。另外,在我執行'&'後臺處理並按回車讓myshell>恢復後,它會打印一個myshell>,然後在下一次輸入中選擇seg錯誤。如果我沒有解釋清楚,我很抱歉,但這真的讓我發瘋。如果您有任何建議,請告訴我。

#include <stdio.h> 
#include <iostream> 
#include <unistd.h> 
#include <cstdlib> 
#include <cstring> 
#include <sys/types.h> 
#include <cstdio> 
#include <sys/wait.h> 
#include <stdio.h> 

/*Function that parses the command the user inputs. 
    It takes myArgv and myString as inputs. 
    It returns the value of exitcond, which is used to see if the user wants to exit or not. 
    Also, this is where myString is tokenized using strok()*/ 
int parseCommand(char *myArgv[10], char myString[255]) 
{ 
    int exitcond=0; 
    if((strcmp(myArgv[0], "exit") == 0)||(strcmp(myArgv[0], "quit")==0)) 
    { 
     exitcond = 1; 
     return exitcond; 
    } 
    int i; 
    char *token; 
    token = strtok(myString," "); 
     i=0; 
     while (token != NULL) 
     { 
      myArgv[i] = token; 
      token = strtok(NULL," "); 
      i++; 
     } 

     /* 
     * Set the last entry our new argv to a null char 
     * (see man execvp to understand why). 
     */ 
     myArgv[i] = '\0'; 
    return exitcond; 
} 
/*Function that gets the command from the user and sees if they want 
background processing or not (presence of '&'). 
    It takes inputs of choose and myString. choose is the variable for 
whether background processing is necessary or not, while myString is 
an empty character array. 
    It outputs the value of the choose variable for lter use.*/ 
int getCommand(int choose, char myString[255]) 
{ 
    int i; 
    choose=0; 
    fgets(myString, 256, stdin); 
    if (myString[0]=='\0') 
    { 
     choose=0; 
     return choose; 
    } 
     for (i=0; myString[i]; i++) 
     { 
      if (myString[i]== '&') 
      { 
      choose=1; 
      myString[i]=' '; 
     } 

     if (myString[i] == '\n') 
     { 
      myString[i] = '\0'; 
     } 
    } 
    return choose; 
} 
/*Main function where all the calling of other functions and processes 
is done. This is where the user enters and exits the shell also. All 
usage of fork, pid, waitpid and execvp is done here.*/ 
int main() 
{ 
    using namespace std; 
    int exitCondition=0, i=0, status; 
    char myString[255]; 
    char *token, *myArgv[10]; 
    pid_t pid, waiting; 
    int bg=0; 
    while (!exitCondition) 
    { 
     /* print a prompt and allow the user to enter a stream of characters */ 
     cout << "myshell> "; 
     bg=0; 
     int choose=0; 
     bg=getCommand(choose,myString); 
     exitCondition=parseCommand(myArgv,myString); 
     if(exitCondition==1) 
     { 
      cout<<"Thank you for using my shell.\n"; 
     } 

    else { 
    /* while (myString[0]=='\0') 
     { 
      cout<<"myshell> "; 
      bg=getCommand(choose,myString); 
     }*/ 
     /* The user has a command, so spawn it in a child process */ 

     pid = fork(); 

     if (pid == -1) 
     { 
      /* to understand why this is here, see man 2 fork */ 
      cout << "A problem arose, the shell failed to spawn a child process" << endl; 
      return(1); 
     } 

     else if (pid == 0) 
     { 
      // Child process 
      execvp(myArgv[0],myArgv); 
      cout << "Bad command or file name, please try again!\n" << endl; 
      return 0; 
     } else { 
      /* This makes sure that the spawned process is run in the foreground, 
       because the user did not choose background */ 
      if(bg==0) 
      { 
       waitpid(pid,NULL,0); 

      } 
      } 

    } 
} 
return 0; 
} 
+0

運行你的'valgrind'計劃將幫助您跟蹤段錯誤。 – immibis

+0

是我運行的外部程序,還是它的一部分? – whoisthis88

+1

外部程序:http://valgrind.org/如果它沒有與你的開發工具一起安裝,它通常在任何* nix發行版的倉庫中可用。 Windows ...並非如此。可能對使用'fork'的人來說不是問題。 – user4581301

回答

0

好的,你有三個錯誤,其中一個導致段錯誤。在其他人中,人們會將數組中的垃圾參數傳遞給execvp,另一個會泄漏後臺作業的殭屍進程。

我已經與其中的錯誤都與修復以及修正代碼並註明其[請原諒無償風格清理]:

#include <cstdio> 
#include <iostream> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 

#define AVCOUNT  100 
#define STRBUFLEN 2000 

/*Function that parses the command the user inputs. 
    It takes myArgv and myString as inputs. 
    It returns the value of exitcond, which is used to see if the user wants to 
    exit or not. 
    Also, this is where myString is tokenized using strok()*/ 
int 
parseCommand(char **myArgv, char *myString) 
{ 
    char *token; 
    char *bp; 
    int exitcond = 0; 
    int i; 

    // NOTE/BUG: original check for exit/quit was here -- at this point 
    // myArgv is undefined (hence the segfault) 

    // NOTE/BUG: your original loop -- at the end i was one beyond where it 
    // should have been so that when myArgv gets passed to execvp it would 
    // have an undefined value at the end 
#if 0 
    token = strtok(myString, " "); 
    i = 0; 
    while (token != NULL) { 
     myArgv[i] = token; 
     token = strtok(NULL, " "); 
     i++; 
    } 
#endif 

    // NOTE/BUGFIX: here is the corrected loop 
    i = 0; 
    bp = myString; 
    while (1) { 
     token = strtok(bp, " "); 
     bp = NULL; 

     if (token == NULL) 
      break; 

     myArgv[i++] = token; 
    } 

    /* 
    * Set the last entry our new argv to a null pointer 
    * (see man execvp to understand why). 
    */ 
    // NOTE/BUG: with your code, i was one too high here 
    myArgv[i] = NULL; 

    // NOTE/BUGFIX: moved exit/quit check to here now that myArgv is valid 
    token = myArgv[0]; 
    if (token != NULL) { 
     if ((strcmp(token, "exit") == 0) || (strcmp(token, "quit") == 0)) 
      exitcond = 1; 
    } 

    return exitcond; 
} 

/*Function that gets the command from the user and sees if they want 
    background processing or not (presence of '&'). 
    It takes inputs of choose and myString. choose is the variable for 
    whether background processing is necessary or not, while myString is 
    an empty character array. 
    It outputs the value of the choose variable for lter use.*/ 
int 
getCommand(int choose, char *myString) 
{ 
    int i; 

    choose = 0; 
    fgets(myString, STRBUFLEN, stdin); 

    if (myString[0] == '\0') { 
     choose = 0; 
     return choose; 
    } 

    for (i = 0; myString[i]; i++) { 
     if (myString[i] == '&') { 
      choose = 1; 
      myString[i] = ' '; 
     } 

     if (myString[i] == '\n') { 
      myString[i] = '\0'; 
      break; 
     } 
    } 

    return choose; 
} 

/*Main function where all the calling of other functions and processes 
    is done. This is where the user enters and exits the shell also. All 
    usage of fork, pid, waitpid and execvp is done here.*/ 
int 
main() 
{ 
    using namespace std; 
    int exitCondition = 0; 
    int status; 
    char myString[STRBUFLEN]; 
    char *myArgv[AVCOUNT]; 
    pid_t pid; 
    int bg = 0; 

    while (!exitCondition) { 
     // NOTE/BUGFIX: without this, any background process that completed 
     // would become a zombie because it was never waited for [again] 
     // reap any finished background jobs 
     while (1) { 
      pid = waitpid(0,&status,WNOHANG); 
      if (pid < 0) 
       break; 
     } 

     /* print a prompt and allow the user to enter a stream of characters */ 
     cout << "myshell> "; 
     bg = 0; 
     int choose = 0; 

     bg = getCommand(choose, myString); 

     exitCondition = parseCommand(myArgv, myString); 
     if (exitCondition == 1) { 
      cout << "Thank you for using my shell.\n"; 
      break; 
     } 

     /* while (myString[0]=='\0') { cout<<"myshell> "; bg=getCommand(choose,myString); } */ 
     /* The user has a command, so spawn it in a child process */ 

     pid = fork(); 

     if (pid == -1) { 
      /* to understand why this is here, see man 2 fork */ 
      cout << "A problem arose, the shell failed to spawn a child process" << endl; 
      return 1; 
     } 

     if (pid == 0) { 
      // Child process 
      execvp(myArgv[0], myArgv); 
      cout << "Bad command or file name, please try again!\n" << endl; 
      return 1; 
     } 

     /* This makes sure that the spawned process is run in the 
     foreground, because the user did not choose background */ 
     if (bg == 0) 
      waitpid(pid, &status, 0); 
    } 

    return 0; 
}