在unix C shell中使用fork()的意外行为

时间:2014-10-01 08:34:30

标签: c shell unix fork execvp

当我在loading()函数之外分叉并调用execvp()时,一切似乎都正常工作。但是我想检测输入是否是有效命令,如果命令有效则返回整数。这样我就可以将命令添加到我的历史记录中,如果它是一个有效的命令。然后,我可以使用" r N"运行历史记录中的命令,其中N是任意数字,然后再次在我的历史记录中重新存储该命令(不是" r N")。在我将fork()移动到setup()函数之前,一切都按照我想要的方式工作。既然我在setup()中分叉和运行命令,那么我发现了两件我不理解的事情。

1)如果我输入一个命令和一些标志,只有命令存储在历史记录中并且标志被切断。我不知道旗帜被移除的位置 2)如果我键入" r N"在历史中运行第N个命令," r N"存储回历史而不是第N个命令。

所以例如:
在将fork()和execvp()移动到setup()之前,我可以输入ls -al &,整个字符串将存储到*history[]中。现在只存储了ls。 然后,如果我想重新运行该命令,我可以键入
r 1ls -al &将再次添加到*history[]数组中。现在r 1已被添加到历史记录中。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> // to use system calls
#include <string.h> // to use string functions
#include <signal.h> // to handle Ctrl-C

#define MAX_LINE 80
#define BUFFER_SIZE 50
char *history[MAX_LINE];    // a list to hold each command entered
int cmdNum = 0;             // keep track of the number of commands entered

void handle_SIGINT() {      // if control C is detected then list the last 10 input commands
    int displayNum = cmdNum;
    printf("\n");
    int i = 10;
    int j = cmdNum -1;
    while(i > 0 && j > -1) {
        printf("%d %s\n", displayNum, history[j]);
        i--;
        j--;
        displayNum--;
    }
}

/* setup() reads in the next command line, separateing it into distinct
 * tokens using whitespace as delimiters. setup() modifies the args
 * parameter so that it holds pointers to the null-terminated strings
 * that are the tokens in the most recent user command line as well
 * as NULL pointer, indicating the end of the argument list. setup()
 * calls exit() when Controld-D is entered. */

int setup(char inputBuffer[], char *args[], int *background) {

    char *hasInput = fgets(inputBuffer, MAX_LINE, stdin);   // grab user input
    char *arguments;                                        // temporary string to hold input as it's split into separate strings
    int i = 0;                                              // place holder for moving forward in args[]


    char last = inputBuffer[(strlen(inputBuffer)-1)];       // check the last character in inputBuffer
    if(last == '&') {                                       // is the last character '&'
        *background = 1;
    }

    if(hasInput == NULL) {                                  // check for control D and exit
        printf("\n");
        exit(EXIT_SUCCESS);
    } else if(strlen(inputBuffer) < 2) {                    // is inputBuffer empty?
        return 0;
    } else {
        inputBuffer = strtok(inputBuffer, "\n");            // remove \n from the end of the inputBuffer
        char *forHistory = strdup(inputBuffer);             // make a copy of the input because its original value gets modified
        char *tempInput = strdup(inputBuffer);              // copy input again because it gets modified after strtok
        arguments = strtok(tempInput, " \n");               // get the first string from inputBuffer before a " " appears
        if (*arguments == 'r') {                            // is the first string just the character 'r'?
            arguments = strtok(NULL, " \n");                // resume splitting input buffer from where it left off
            char *safetyString;
            if((strtol(arguments, &safetyString, 10) -1) > -1 &&        // take the next argument after the letter r and turn it into an integer
                    (strtol(arguments, &safetyString, 10) -1) <= cmdNum -1 &&
                    history[0] != 0) {
                int cmdToRun = strtol(arguments, &safetyString,10) - 1; // subtrack 1 from the integer since arrays start at 0
                inputBuffer = strdup(history[cmdToRun]);                // inputBuffer should be equal to a runnable command from history and not "r #"
                forHistory = strdup(history[cmdToRun]);                 // change forHistory as well, because we don't want to save "r #" in history
                printf("forHistory: %s\n", forHistory);
            } else {
                printf("Command not in history.\n");
                return 0;
            }
        }

        if(*background == 1) {                              // remove the '&' from inputBuffer before storing each string in args
            inputBuffer[(strlen(inputBuffer)-1)] = 0;
        }

        arguments = strtok(inputBuffer, " \n");             // get the first string from inputBuffer before a " "
        while(arguments != NULL) {
            args[i] = arguments;                            // place each space separated string into args
            arguments = strtok(NULL, " \n");                // resume splitting inputBuffer from where it left off
            i++;
        }
        args[i] = 0;                                        // add the null terminator to args

        pid_t pid;
        int status;

        if((pid = fork()) < 0) {            // make sure fork() works
        } else if(pid == 0) {               // if this is a child process
            if(execvp(args[0], args) < 0) { // try to execute command
                // if the program gets here then execvp() failed
                printf("execvp failed, ivalid command\n");
                return 0;
                //exit(EXIT_FAILURE);
            }
        } else if(*background == 0) {       // unless specified wait for the the child to finish
            while(wait(&status) != pid);
            inputBuffer = strdup(forHistory);
            //printf("inputBuffer after = forHistory: %s\n", inputBuffer);
            return 1;
        }
    }
}

int main(void) {
    char inputBuffer[MAX_LINE];     // buffer to hold command entered
    int background;                 // equals 1 if a command is folled by '&'
    char *args[MAX_LINE/2+1];       // command line arguments

    while(1) {
        background = 0;
        printf("COMMAND->");

        struct sigaction handler;
        handler.sa_handler = handle_SIGINT;
        sigaction(SIGINT, &handler, NULL);
        siginterrupt(SIGINT, 0);


        if(setup(inputBuffer, args, &background)) {
            //printf("inputBuffer before history placement: %s\n", inputBuffer);
            history[cmdNum] = strdup(inputBuffer);              // copy the comand to history
            cmdNum++;
        }
        /* the steps are:
         * (1) fork a child process using fork()
         * (2) the child process will invoke execvp()
         * (3) if background == 0, the parent will wait,
         * otherwise it will invoke the setup() function again */
    }
}

0 个答案:

没有答案