【问题标题】:C - Adding CD to Shell [closed]C - 将 CD 添加到 Shell [关闭]
【发布时间】:2017-09-22 22:37:20
【问题描述】:

我正在阅读 Silberschatz、Galvin 和 Gagne 的操作系统概念第 9 版。我已经进入第 3 章的第一个项目,他们要求我们创建一个 UNIX Shell 和历史特性。我已经创建了其中的一些,历史记录和我相信的大多数 shell 命令(密码、日期、cal 等)都可以工作 - 我现在正在尝试将 cd 添加到列表中,当我得到 Segmentation fault (core dumped) 时我在我的外壳中使用cd。我觉得这实现起来并不难,你只要把pwd 和你要去的地方换掉就行了。这是我的代码:

//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

理想情况下,当我输入我的 shell ./script.sh 时,它会(或者我想我想要它)运行脚本文件,但目前只是得到错误,因为我还没有实现它。如果有人可以帮助我解决这两件事,我将不胜感激!

【问题讨论】:

    标签: c shell


    【解决方案1】:

    如果您忽略所有历史管理,仍然会出现此问题。如果您使用静态 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() 时才会发生这种情况:子进程无法更改其父进程的目录。如果您打算这样做,请不要分叉。

    我在下面的完整 shell 转储中添加了这两个修复程序,下面是一个展示它如何工作的会话:

    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);
                     }
                 }
             }
         }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-06-20
      • 2016-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-05
      • 1970-01-01
      相关资源
      最近更新 更多