2017-09-22 55 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); 
       } 
      } 
     } 
    } 
}