【问题标题】:The shell I am writing does not exit correctly after execvp() failsexecvp() 失败后,我正在编写的 shell 无法正确退出
【发布时间】:2014-05-08 15:02:17
【问题描述】:

我有一个任务是用 C 代码制作一个 shell,并且我有一个大多数时间都可以使用的解决方案。如果程序存在,我的解决方案就可以工作,我可以使用 Control-D 或键入 exit 退出我的 shell。但是当我尝试一个我知道不存在的命令时,我的 shell 将打印一条错误消息,指出找不到命令,但我必须键入 exit 或按 Control-D 相同的次数由于输入了无效命令,即如果我输入了 3 次错误命令,则必须按 Control-D 3 次。我真的不知道这里发生了什么。我检查了所有变量,当我按下 Control-D 时读取为 -1,但似乎跳过了 if 语句。

这是我认为问题所在的源代码部分:

comp20200Shell.c

#include "comp20200Shell_header.h"
#include <signal.h>


/*
 * Name: ****
 * Student Number: ****
 * Email: ****
 *
 * This is the main function of my shell implementation.
 *
 */
int main(void)
{
    bool end_program = false;
    size_t length = 0;
    ssize_t read;
    char* current_directory = NULL;
    char* current_time = NULL;

    /* Sets up signal handler to catch SIGINT*/
    if(signal(SIGINT, sigintHandler) == SIG_ERR)
    {
        error("An error occured while setting a signal handler\n");
    }

    /* Infinitive loop, so after command or invalid comman will prompt again*/
    while(end_program != true)
    {
        char* input = NULL;

        /* Gets current working directory */
        current_directory = return_current_directory();

        /* Gets current date and time */
        current_time = return_time();

        /* Prints Prompt */
        printf("%s\x5b%s\x5d %s%s %s%s%s", MAGENTA_TEXT, current_time, GREEN_TEXT, current_directory, BLUE_TEXT, PROMPT, RESET_COLOUR);

        /* Frees the pointers returned by return_time() and return_current_directory() */
        free(current_time);
        free(current_directory);

        /* Reads one line from standard input */
        read = getline(&input, &length, stdin);

        /* Checks if ctrl d, i.e. end of file is found or exit is typed */
        if(strcmp(input, "exit\n") == 0 || read == -1)
        {
            if(read == -1)
            {
                putchar('\n');
            }
            /* Frees input */
            free(input);
            return(0);
        }

        /* Removes newline character that will be at the end */
        remove_trailing_newline(input);

        /* Passes input to process input, and the return value is passed in to process errors */
        process_errors(process_input(&input));

        /* Frees input */
        free(input);
    }

    return(0);
}

process_input.c

#include "comp20200Shell_header.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/*
 * Name: ****
 * Student Number: ****
 * Email: ****
 *
 * This function is used to process the command entered by the user
 *
 * return: the error value or 0 when everything whent ok
 * arguments: the command entered by the user
 *
 */
int process_input(char** input)
{
    bool redirect_stdout = false;
    bool redirect_stderr = false;

    pid_t child_pid;
    int child_status;
    char** argument = malloc(sizeof(char*));
    int count = 0;

    char* temp = strtok(*input, " ");
    while(temp != NULL)
    {
        argument[count] = temp;
        count ++;
        argument = realloc(argument, (count+2) * sizeof(char *));
        temp = strtok(NULL, " ");
    }
    argument[count] = NULL;

    if(argument[0] == NULL)
    {
        return(0);
    }
    else if(strcmp(argument[0], "cd") == 0)
    {
        return(change_directory(argument[1]));
    }

    int index;
    for(index = 1; argument[index] != NULL; index++)
    {
        if(strcmp(argument[index], ">0") == 0)
        {
            if(argument[index + 1] == NULL)
            {
                return(EINVAL);
            }
            redirect_stdout = true;
            break;
        }
        else if(strcmp(argument[index], ">2") == 0)
        {
            if(argument[index + 1] == NULL)
            {
                return(EINVAL);
            }
            redirect_stderr = true;
            break;
        }
    }


    child_pid = fork();
    if(child_pid == 0)
    {
        int file;
        if(redirect_stdout == true)
        {
            file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
            dup2(file, 1);
            edit_arguments(argument, index);
            execvp(argument[0], argument);
            return(-1);
        }
        else if(redirect_stderr == true)
        {
            file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
            dup2(file, 2);
            edit_arguments(argument, index);
            execvp(argument[0], argument);
            return(-1);
        }

        execvp(argument[0], argument);
        return(-1);
    }
    else
    {
        wait(&child_status);
    }

    return(child_status);
}

comp20200Shell_header.h

/*
 * Name: ****
 * Student Number: ****
 * Email: ****
 *
 * This is my header file, It includes all common used headerfiles on the top.
 * Any specific header file that is only used once will be included with the .c file that needs it.
 *
 */

/* included headerfiles begin */
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
/* included headerfiles end */

/* defenitions begin */
#define PROMPT "# "
#define BUFFER_SIZE 1024
#define BLUE_TEXT "\x1B[34m"
#define MAGENTA_TEXT "\x1B[35m"
#define GREEN_TEXT "\x1B[32m"
#define RESET_COLOUR "\x1B[0m"
/* defenitions end */

/* Function prototypes begin */
void remove_trailing_newline(char *input);
void sigintHandler(int sig_num);
int process_input(char** input);
char* return_time(void);
void error(const char *fmt, ...);
int change_directory(char* path);
char* return_current_directory(void);
void process_errors(int return_value);
void edit_arguments(char** argument, int index);
/* Function prototypes end */

我省略了其余的源代码,因为我认为问题不存在。

【问题讨论】:

    标签: c linux shell execvp


    【解决方案1】:

    如果由于某些错误而无法执行,您可以使用 exit(1) 或 exit (-1) 退出该部分,而不是 return -1。

    【讨论】:

      【解决方案2】:

      您应该使用exit(1); 或等效的而不是return(-1);。您可能想要使用_exit(1);_exit(255);(或_exit(-1);,但它等效于_exit(255);)。您可能希望在退出之前将错误消息打印到标准错误。

      当你不退出时,你会得到两个,然后是三个,然后是 N 个 shell,它们都试图从终端读取输入。您必须通过 Control-D 指示 EOF 来分别退出每个。如果您尝试输入命令,那么 shell 会获取每个字符,这将导致混乱(和严重的危险;您可能以为您输入了 grep fungible form.file | tr -d 'fr' &gt; /tmp/x33,但如果其中一个 shell 得到 rm -fr /,那么您有麻烦了!)。

      【讨论】:

      • 孩子和父亲最终不会在他的实现中发生争执,因为他不支持后台作业(即,父亲在分叉后会执行 `wait(&child_status);`)。
      【解决方案3】:

      在您的孩子中,在调用execvp 之后,您需要调用exit(EXIT_FAILURE); 而不是return -1;。否则您的孩子将继续运行,并将解释下一个命令(这就是为什么您需要退出 N 次,其中 N 是您尝试调用的不存在命令的数量)。

      更改后,您的父进程将看到子进程以非零返回码终止,并应解释错误码。没有真正的方法来区分失败是来自execvp(由于不存在的命令)还是来自调用的进程。如果exit 之前的孩子中有一个错误,我建议从execvp 打印错误。

      请注意,如果execvp 成功,它将永远不会返回,因此调用execvp 之后的代码只有在命令失败时才能执行。

      所以,我的建议是这样做:

      if(child_pid == 0)
      {
          int file;
          if(redirect_stdout == true)
          {
              file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
              dup2(file, 1);
              edit_arguments(argument, index);
              execvp(argument[0], argument);
              perror("execvp");
              exit(EXIT_FAILURE);
          }
          else if(redirect_stderr == true)
          {
              file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
              dup2(file, 2);
              edit_arguments(argument, index);
              execvp(argument[0], argument);
              perror("execvp");
              exit(EXIT_FAILURE);
          }
      
          execvp(argument[0], argument);
          perror("execvp");
          exit(EXIT_FAILURE);
      }
      else
      {
          wait(&child_status);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-01-13
        • 2020-06-27
        • 2019-06-27
        • 1970-01-01
        • 2018-08-30
        • 2016-06-05
        • 1970-01-01
        相关资源
        最近更新 更多