繁体   English   中英

在 C 中设计 shell 以执行由逻辑和连接的命令的问题

[英]Problem in designing a shell in C to execute commands connected by logical and

我正在用 C 编写一个简单的 shell 来执行像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;
}

我的动机是:

每当 shell 接收到外部命令时,它就会派生自己,比如新进程是 1 并且命令是command1 & command2 1 再次分叉自己执行 command1 和 command2。 如果command1成功执行,则只为command2创建一个新进程,否则不会。

问题是,即使最后一条命令是正确的,也永远不会执行。 我无法弄清楚代码中的问题。

如果需要任何信息,请在下面发表评论。

在这个循环中

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

您将字符串的所有部分复制到arg除了最后一部分。

对于命令行中的最后一个标记,如果标记后没有额外空间,您将获得指向cmd的有效指针,但为nextNULL ,从而使该标记未处理。

您还注释掉了execv系列函数所需的arg数组的execv

您可能会重新设计循环:

        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 个时,第二个版本才能正常工作。 对于最大长度命令,它将杀死最后一个字符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM