2017-09-22 96 views
-3

我正在關注Silberschatz,Galvin和Gagne的操作系統概念第9版。我已經到了第3章的第一個項目,他們要求我們創建一個UNIX Shell和歷史記錄功能。我創建了一些歷史記錄和大多數shell命令我認爲(pwd,date,cal等) - 我正在嘗試將cd添加到列表中,並且在使用cd時,我得到Segmentation fault (core dumped)我的外殼。我覺得這不難實現,你只需要pwd並將它與任何你想去的地方互換。這裏是我的代碼:C - 向外殼添加CD

//Enter command 'history' for history feature and CTRL - c to exit the 'osh>' shell 
/*Header files */ 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <wait.h> 

#define MAX_LINE 80 /* The maximum length of a command */ 
#define BUFFER_SIZE 50 
#define buffer "\n\Shell Command History:\n" 

//declarations 
char history[10][BUFFER_SIZE]; //history array to store history commands 
int count = 0; 
char *gdir, *dir, *to; 

//function to display the history of commands 
void displayHistory() { 

    printf("Shell command history:\n"); 

    int i; 
    int j = 0; 
    int histCount = count; 

    //loop for iterating through commands 
    for (i = 0; i<10;i++) { 
     //command index 
     printf("%d. ", histCount); 
     while (history[i][j] != '\n' && history[i][j] != '\0') {  
      //printing command 
      printf("%c", history[i][j]); 
      j++; 
     } 
     printf("\n"); 
     j = 0; 
     histCount--; 
     if (histCount == 0) 
      break; 
    } 
    printf("\n"); 
    } 



    //Fuction to get the command from shell, tokenize it and set the args parameter 

    int formatCommand(char inputBuffer[], char *args[],int *flag) { 
    int length; // # of chars in command line 
    int i; // loop index for inputBuffer 
    int start; // index of beginning of next command 
    int ct = 0; // index of where to place the next parameter into args[] 
    int hist; 
    //read user input on command line and checking whether the command is !! or !n 
      length = read(STDIN_FILENO, inputBuffer, MAX_LINE);  


    start = -1; 
    if (length == 0) 
     exit(0); //end of command 
    if (length < 0) { 
     printf("Command not read\n"); 
     exit(-1); //terminate 
    } 

    //examine each character 
    for (i=0;i<length;i++) { 
     switch (inputBuffer[i]) { 
      case ' ': 
      case '\t' : // to seperate arguments 
      if(start != -1) { 
       args[ct] = &inputBuffer[start]; 
       ct++; 
      } 
      inputBuffer[i] = '\0'; // add a null char at the end 
      start = -1; 
      break; 

      case '\n': //final char 
      if (start != -1) { 
       args[ct] = &inputBuffer[start]; 
       ct++; 
      } 
      inputBuffer[i] = '\0'; 
      args[ct] = NULL; // no more args 
      break; 

      default : 
      if (start == -1) 
       start = i; 
      if (inputBuffer[i] == '&') { 
       *flag = 1; //this flag is the differentiate whether the child process is invoked in background 
       inputBuffer[i] = '\0'; 
      } 
     } 
    } 

    args[ct] = NULL; //if the input line was > 80 

    if(strcmp(args[0],"history")==0) {  
     if(count>0) { 
      displayHistory(); 
     } else { 
      printf("\nNo Commands in the history\n"); 
     } 
     return -1; 
    } else if (args[0][0]-'!' ==0) {  
     int x = args[0][1]- '0'; 
     int z = args[0][2]- '0'; 

     if(x>count) { // second letter check 
      printf("\nNo Such Command in the history\n"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else if (z!=-48) { // third letter check 
      printf("\nNo Such Command in the history. Enter <=!9 (buffer size is 10 along with current command)\n"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else { 
      if(x==-15) {  
       strcpy(inputBuffer,history[0]); // this will be your 10 th(last) command 
     } else if(x==0) { //Checking for '!0' 
      printf("Enter proper command"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else if(x>=1) { //Checking for '!n', n >=1 
      strcpy(inputBuffer,history[count-x]); 
     }  
    } 
    } 

    for (i = 9;i>0; i--) //Moving the history elements one step higher 
     strcpy(history[i], history[i-1]); 

    strcpy(history[0],inputBuffer); //Updating the history array with input buffer 
    count++; 
    if(count>10) { 
     count=10; 
    } 
} 

int main(void) { 
    char inputBuffer[MAX_LINE]; /* buffer to hold the input command */ 
    int flag; // equals 1 if a command is followed by "&" 
    char *args[MAX_LINE/2 + 1];/* max arguments */ 
    int should_run =1; 

    pid_t pid,tpid; 
    int i; 

    while (should_run) { //infinite loop for shell prompt 
     flag = 0; //flag =0 by default 
     printf("osh>"); 
     fflush(stdout); 

     if(-1!=formatCommand(inputBuffer,args,&flag)) { // get next command 
      pid = fork(); 

      if (!strcmp(args[0], "cd")) { 
       gdir = getcwd(inputBuffer, sizeof(inputBuffer)); 
       dir = strcat(gdir, "/"); 
       to = strcat(dir, args[1]); 

       chdir(to); 
       continue; 
      } 

      if (pid < 0) { // if pid is less than 0, forking fails 
       printf("Fork failed.\n"); 
       exit (1); 
      } else if (pid == 0) { //if pid == 0 
       //command not executed 
       if (execvp(args[0], args) == -1) { 
        printf("Error executing command\n"); 
       } 
      } else { 
      // if flag == 0, the parent will wait, 
      // otherwise returns to the formatCommand() function. 
       i++; 
       if (flag == 0) { 
        i++; 
        wait(NULL); 
       } 
      } 
     } 
    } 
} 

我也堅持如何添加批處理,以便我可以運行我的shell腳本。我有一個文件:script.sh具有代碼:

pwd 
cal 
date 

理想的情況下,當我鍵入我的殼./script.sh它會(或者我猜我希望它)運行該腳本文件,但目前只是得到錯誤,因爲我的天堂」 t實現了這一點。如果有人能幫助我,我會堅持這兩件事,我會很感激!

回答

5

如果省略所有歷史管理,則仍會出現此問題。如果您使用靜態cd命令而不是從用戶讀取它,也會發生這種情況。如果你保持刪除所有未實際需要的東西,你可能會想出這個小小的例子仍然顯示了同樣的問題:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

int main() { 
    char str[50]; 
    // Pretend to read input 
    strcpy(str, "cd tmp"); 
    // Pretend to split string 
    str[2]='\0'; 

    // Pretend to set up arguments 
    char* args[2]; 
    args[0] = &str[0]; 
    args[1] = &str[3]; 

    // Your code for chdir: 
    char* gdir = getcwd(str, sizeof(str)); 
    char* dir = strcat(gdir, "/"); 

    // Why does this segfault? 
    char* to = strcat(dir, args[1]); 

    chdir(to); 
    perror("Result"); 
} 

這樣做的問題是,args[1]to實際上是同一塊記憶。當你將一個角色附加到另一個角色時,你還會在另一個角色上附加一個角色。這意味着一次只複製一個角色意味着你永遠不會完成,而是會發生段錯誤。

而是直接將相關目錄傳遞給chdir。所有的系統調用接受相對路徑:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

int main() { 
    char str[50]; 
    // Pretend to read input 
    strcpy(str, "cd tmp"); 
    // Pretend to split string 
    str[2]='\0'; 

    // Pretend to set up arguments 
    char* args[2]; 
    args[0] = &str[0]; 
    args[1] = &str[3]; 

    // Doesn't segfault 
    chdir(args[1]); 
    // Prints "Result: Success" 
    perror("Result"); 
} 

如果解決這個問題,你會發現,它停止和段錯誤是perror聲稱它是成功的。

但是,將此應用於您的程序似乎可以正常工作,但不會更改目錄並報告成功,但您仍然位於同一目錄中。

如果您再次縮小範圍,則會發現只有在您首先執行fork()時纔會發生:子進程無法更改其父母的目錄。如果你打算這麼做,不要分叉。

我加在你的全外殼都修復轉儲下面,這裏是呈現出會話它是如何工作:

osh>pwd 
/
osh>cd tmp 
osh>pwd 
/tmp 

下面是與修復的完整源代碼附:

//Enter command 'history' for history feature and CTRL - c to exit the 'osh>' shell 
/*Header files */ 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <wait.h> 

#define MAX_LINE 80 /* The maximum length of a command */ 
#define BUFFER_SIZE 50 
#define buffer "\n\Shell Command History:\n" 

//declarations 
char history[10][BUFFER_SIZE]; //history array to store history commands 
int count = 0; 
char *gdir, *dir, *to; 

//function to display the history of commands 
void displayHistory() { 

    printf("Shell command history:\n"); 

    int i; 
    int j = 0; 
    int histCount = count; 

    //loop for iterating through commands 
    for (i = 0; i<10;i++) { 
     //command index 
     printf("%d. ", histCount); 
     while (history[i][j] != '\n' && history[i][j] != '\0') { 
      //printing command 
      printf("%c", history[i][j]); 
      j++; 
     } 
     printf("\n"); 
     j = 0; 
     histCount--; 
     if (histCount == 0) 
      break; 
    } 
    printf("\n"); 
    } 



    //Fuction to get the command from shell, tokenize it and set the args parameter 

    int formatCommand(char inputBuffer[], char *args[],int *flag) { 
    int length; // # of chars in command line 
    int i; // loop index for inputBuffer 
    int start; // index of beginning of next command 
    int ct = 0; // index of where to place the next parameter into args[] 
    int hist; 
    //read user input on command line and checking whether the command is !! or !n 
      length = read(STDIN_FILENO, inputBuffer, MAX_LINE); 


    start = -1; 
    if (length == 0) 
     exit(0); //end of command 
    if (length < 0) { 
     printf("Command not read\n"); 
     exit(-1); //terminate 
    } 
    //examine each character 
    for (i=0;i<length;i++) { 
     switch (inputBuffer[i]) { 
      case ' ': 
      case '\t' : // to seperate arguments 
      if(start != -1) { 
       args[ct] = &inputBuffer[start]; 
       ct++; 
      } 
      inputBuffer[i] = '\0'; // add a null char at the end 
      start = -1; 
      break; 

      case '\n': //final char 
      if (start != -1) { 
       args[ct] = &inputBuffer[start]; 
       ct++; 
      } 
      inputBuffer[i] = '\0'; 
      args[ct] = NULL; // no more args 
      break; 

      default : 
      if (start == -1) 
       start = i; 
      if (inputBuffer[i] == '&') { 
       *flag = 1; //this flag is the differentiate whether the child process is invoked in background 
       inputBuffer[i] = '\0'; 
      } 
     } 
    } 

    args[ct] = NULL; //if the input line was > 80 

    if(strcmp(args[0],"history")==0) { 
     if(count>0) { 
      displayHistory(); 
     } else { 
      printf("\nNo Commands in the history\n"); 
     } 
     return -1; 
    } else if (args[0][0]-'!' ==0) { 
     int x = args[0][1]- '0'; 
     int z = args[0][2]- '0'; 

     if(x>count) { // second letter check 
      printf("\nNo Such Command in the history\n"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else if (z!=-48) { // third letter check 
      printf("\nNo Such Command in the history. Enter <=!9 (buffer size is 10 along with current command)\n"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else { 
      if(x==-15) { 
       strcpy(inputBuffer,history[0]); // this will be your 10 th(last) command 
     } else if(x==0) { //Checking for '!0' 
      printf("Enter proper command"); 
      strcpy(inputBuffer,"Wrong command"); 
     } else if(x>=1) { //Checking for '!n', n >=1 
      strcpy(inputBuffer,history[count-x]); 
     } 
    } 

    } 

    for (i = 9;i>0; i--) //Moving the history elements one step higher 
     strcpy(history[i], history[i-1]); 

    strcpy(history[0],inputBuffer); //Updating the history array with input buffer 
    count++; 
    if(count>10) { 
     count=10; 
    } 
} 

int main(void) { 
    char inputBuffer[MAX_LINE]; /* buffer to hold the input command */ 
    int flag; // equals 1 if a command is followed by "&" 
    char *args[MAX_LINE/2 + 1];/* max arguments */ 
    int should_run =1; 

    pid_t pid,tpid; 
    int i; 

    while (should_run) { //infinite loop for shell prompt 
     flag = 0; //flag =0 by default 
     printf("osh>"); 
     fflush(stdout); 

     if(-1!=formatCommand(inputBuffer,args,&flag)) { // get next command 

      // Don't fork first 
      if (!strcmp(args[0], "cd")) { 
       // Don't fetch the current dir 
       chdir(args[1]); 
       continue; 
      } 

      pid = fork(); 

      if (pid < 0) { // if pid is less than 0, forking fails 
       printf("Fork failed.\n"); 
       exit (1); 
      } else if (pid == 0) { //if pid == 0 
       //command not executed 
       if (execvp(args[0], args) == -1) { 
        printf("Error executing command\n"); 
       } 
      } else { 
      // if flag == 0, the parent will wait, 
      // otherwise returns to the formatCommand() function. 
       i++; 
       if (flag == 0) { 
        i++; 
        wait(NULL); 
       } 
      } 
     } 
    } 
}