Проблема проектирования оболочки в C для выполнения команд, связанных логическими и - PullRequest
0 голосов
/ 16 марта 2020

Я писал простую оболочку в C для выполнения внешних команд, таких как command1 & command2, и вот мой код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

static char line[1024];
char *and, *or, *col;
char *arg[1024];

static char* skipwhite(char* s)
{
        while (isspace(*s)) ++s;
        return s;
}
void run(char *cmd)
{
        *and='\0';
        int stat;
        cmd= skipwhite(cmd);
        char *next= strchr(cmd, ' ');
        int i=0;
        printf("%s \n", cmd);

        while(next!=NULL)
        {
                *next='\0';
                arg[i]=cmd;:
                i++;
                cmd= skipwhite(next+1);
                next= strchr(cmd, ' ');
        }
        //arg[i]='\0';

        int shmid =  shmget(66, sizeof(int), IPC_CREAT | 0777);
        int *status=(int *)shmat(shmid, NULL, 0);
        *status=1;

        int pid=fork();
        if(pid==0)
        {
                int *v=(int *)shmat(shmid, NULL, 0);
                if(execvp(arg[0], arg)==-1)
                {
                        *v=0;
                        _exit(EXIT_FAILURE);
                }
        }
        else
        {
                        waitpid(pid, &stat, 0);
                        if(*status==0)
                                exit(0);
                        //printf("Trying to execute 2nd command\n");
                        cmd=and+1;
                        printf("%s \n", cmd);
                        and = strchr(cmd, '&');
                        if(and==NULL)
                        {
                                cmd= skipwhite(cmd);
                                next = strchr(cmd, ' ');
                                i=0;
                                while(next!=NULL)
                                {
                                        *next='\0';
                                        arg[i]=cmd;
                                        i++;
                                        cmd= skipwhite(next+1);
                                        next= strchr(cmd, ' ');
                                }
                                arg[i]=cmd;
                                //arg[i+1]='\0';

                                if(execvp(arg[0], arg)==-1)
                                        _exit(EXIT_FAILURE);
                                return;
                        }
                        else
                                run(cmd);
        }
}

int main()
{

        int status, pid;
        printf("SIMPLE SHELL made by me. Type 'exit' or send EOF to exit.\n");

        while(1)
        {
                printf("$> ");
                fflush(NULL);

                if(!fgets(line, 1024, stdin))
                        return 0;

                char *cmds= line;
                if(strcmp(cmds, "exit")==0)
                        exit(0);
                and= strchr(cmds, '&');
                or= strchr(cmds, '|');
                col= strchr(cmds, ';');

                if(and!=NULL)
                {
                        pid=fork();
                        if(pid==0)
                                run(cmds);
                        else
                                waitpid(pid, &status, 0);
                }
        }
        return 0;
}

Моя мотивация:

Всякий раз, когда оболочка получает внешняя команда, она разветвляется, говорит, что новый процесс равен 1, а команда - command1 & command2. 1 снова разветвляется для выполнения command1 и command2. Если command1 успешно выполнен, то для command2 создается только новый процесс, иначе нет.

Проблема в том, что последняя команда никогда не выполняется, даже если она правильная. Я не могу понять проблему в коде.

Если требуется какая-либо информация, оставьте комментарий ниже.

1 Ответ

2 голосов
/ 16 марта 2020

В этом l oop

        while(next!=NULL)
        {
                *next='\0';
                arg[i]=cmd;:
                i++;
                cmd= skipwhite(next+1);
                next= strchr(cmd, ' ');
        }
        //arg[i]='\0';

Вы копируете все части строки в arg, кроме последнего.

Для последнего токена в командной строке вы получаете действительный указатель на cmd, но NULL для next, если после токена нет дополнительного пробела, оставляя этот токен необработанным.

Вы также закомментировали часовой для массива arg, который требуется для execv семейства функций.

Вы можете переделать l oop:

        while(cmd && *cmd) {
            arg[i++]=cmd;
            if (next) {
                *next='\0';
                cmd = skipwhite(next+1);
                next = strchr(cmd, ' ');
            }
            else {
                cmd = NULL;        
            }
        }
        arg[i]='\0';

Обновление относительно дополнительного вопроса:

У вас есть '\n' в конец вашей команды, потому что вы не прерываете ее после прочтения командной строки. Функция fgets считывает всю строку в буфер, вплоть до (и включая) '\n', если буфер достаточно большой.

Вам просто нужно удалить его сразу после вызова fgets:

               size_t len = strlen(line);
               if (line[len-1] == '\n')
                        line[len-1] = 0;

или

               line[strlen(line)-1] = 0;

Первая версия также работает нормально, если вы вводите 1023 символа для своих команд, где '\n' не может быть сохранен в буфере.

Вторая версия работает нормально только если вы введете менее 1023 символов. Для команды максимальной длины он убьет последний символ.

...