Linux内核编程实验二

1、实验名称:shell命令解释系统设计实验
2、实验要求:
问题 A:
实现一个能处理前后台运行命令的 shell。
问题 B:
实现一个带有管道功能的 shell。
问题 C:
实现一个能处理 I/O 重定向的 shell。
问题 D:
实现一个能在一行上处理多条命令的 shell。
将问题 A-D 集中到一个 shell 解析程序中。

3、解决思路
第一步解决问题D。一行上多条命令使用 ; 分开,;两边可以有空格,也可以没有,或者都考虑(健壮性),本方案采用没空格。根据 ; 将其拆分成多条命令,逐条执行即可。

第二步解决问题A。让进程在后台运行,意味着父进程不需要等待子进程运行完毕。否则使用waitpid()函数让父进程等待一下即可。

第三步解决输出重定向,即问题C。根据定向符 > 获得后面的文件名。使用open()打开,使用dup2()函数将标准输出重定向到该文件。那么该进程的输出都将添加到文件中,而不是终端。通常在子进程内进行相应操作。

第四步解决输出重定向,问题C。根据输入定向符 < 获得后面文件名。使用open打开。使用dup2将标准输入重定向到该文件,那么该进程将使用该文件作为输入,而不是终端。

第五步解决管道问题。此处使用匿名管道。匿名管道使用pipe(fds)创建。fds为int类型长度为2的数组。fds[0]为读取数据端。fds[1]为写入数据端。创建两个进程一个管道。进程1关闭读端,将标准输出重定向到管道写端,将数据暂时保存在管道内。进程2关闭写端,将标准输入重定向到管道读端,将管道内数据读出,完成进程通信。

不足之处,仅供参考:

缺点一是支持格式有限。比如
普通命令:ls -a /bin //加粗部分必须为全路径
输出重定向:ls /bin > /home/me/shiyan/test.c //>两侧必须空格
输入重定向:cat < /home/me/shiyan/test.c
管道:cat /home/me/shiyan/test.c | sort //管道两端命令不支持出现重定向
//管道符号两侧必须有空格
后台运行:ls /bin > /home/me/shiyan/test.c& //&符号必须紧跟着
多条命令:ls /home;ls /bin //多条命令分号两边无空格
所有测试命令必须严格遵循上面的格式,否则将可能导致错误的运行结果。

另外一个缺点是,没有完整测试所有linux命令,只测试了部分简单常用命令,因此可能部分命令不支持。


4、实现代码

#include <stdio.h>
#include <string.h> //strlen()
#include <unistd.h> //fork()
#include <sys/types.h> //pid_t
#include <sys/wait.h> //waitpid()
#include <stdlib.h> //exit()
#include <fcntl.h> //open()

#define MAXLEN 80
#define MAXPARA 8

int checkpipe(char *command,int comlen)//判断是否包含|
{
    int i;
    for(i=0; i<comlen; i++)
    {
        if(command[i] == '|')
            return i;
    }
    return -1;
}

int checkout(char *command,int comlen)//判断是否包含>
{
    int i;
    for(i=0; i<comlen; i++)
    {
        if(command[i] == '>')
            return 1;
    }
    return 0;
}

int checkin(char *command,int comlen) //判断是否包含<
{
    int i;
    for(i=0; i<comlen; i++)
    {
        if(command[i] == '<')
            return 1;
    }
    return 0;
}

void runcommand(char *command)
{
    int comlen = strlen(command);
    int backrun = 0;
    char *argv1[MAXPARA],*argv2[MAXPARA];
    int index1,index2;
    int i,fd,pipei,fds[2];
    char *filename;
    pid_t p1,p2;

    if(command[comlen-1] == '&')//是否后台运行
    {
        backrun = 1;
        command[comlen-1] = 0;
        comlen--;
    }
    pipei = checkpipe(command,comlen); 
    if(pipei > -1)//有管道
    {
        command[pipei-1] = 0;
        command[pipei] = 0;
        command[pipei+1] = 0;

        index1 = 0;
        argv1[index1++] = command;
        for(i=0; i<pipei-1; i++)
        {
            if(command[i] == ' ')
            {
                command[i] = 0;
                argv1[index1++] = &command[i+1];
            }
        }
        argv1[index1] = NULL;

        i = pipei + 2;
        index2 = 0;
        argv2[index2++] = &command[i];
        for(; i<comlen; i++)
        {
            if(command[i] == ' ')
            {
                command[i] = 0;
                argv2[index2++] = &command[i+1];
            }
        }
        argv2[index2] = NULL;
        //命令处理完毕,建立管道,进程
        pipe(fds);
        p1 = fork();
        if(p1 == 0)
        {
            close(fds[0]);//关闭读端
            close(STDOUT_FILENO);
            dup2(fds[1],STDOUT_FILENO);//重定向
            close(fds[1]);
            execvp(argv1[0],argv1);//该函数执行后进程结束
        }
        else
        {
            if(backrun == 0)
                waitpid(p1,NULL,0);
            p2 = fork();
            if(p2 == 0)
            {
                close(fds[1]);
                close(STDIN_FILENO);
                dup2(fds[0],STDIN_FILENO);
                close(fds[0]);
                execvp(argv2[0],argv2);
            }
            else
            {
                //父进程关闭管道,让两个子进程进行通信
                close(fds[0]);
                close(fds[1]);
                waitpid(p2,NULL,0);
            }
        }
    }
    else
    {
        if(checkout(command,comlen) == 1)//输出重定向
        {
            index1 = 0;
            argv1[index1++] = command;
            for(i=0; i<comlen; i++)
            {
                if(command[i] == ' ')
                {
                    command[i] = 0;
                    if(command[i+1] == '>')
                        break;
                    argv1[index1++] = &command[i+1];
                }
            }
            argv1[index1] = NULL;
            i = i + 3;
            filename = &command[i];

            p1 = fork();
            if(p1 == 0)
            {
                fd = open(filename,O_WRONLY);
                close(STDOUT_FILENO);
                dup2(fd,STDOUT_FILENO);
                close(fd);
                execvp(argv1[0],argv1);
            }
            else
            {
                if(backrun == 0)
                {
                    waitpid(p1,NULL,0);
                }
            }
        }
        else if(checkin(command,comlen) == 1)//输入重定向
        {
            index1 = 0;
            argv1[index1++] = command;
            for(i=0; i<comlen; i++)
            {
                if(command[i] == ' ')
                {
                    command[i] = 0;
                    if(command[i+1] == '<')
                        break;
                    argv1[index1++] = &command[i+1];
                }
            }
            argv1[index1] = NULL;
            i = i + 3;
            filename = &command[i];

            p1 = fork();
            if(p1 == 0)
            {
                fd = open(filename,O_RDONLY);
                close(STDIN_FILENO);
                dup2(fd,STDIN_FILENO);
                close(fd);
                execvp(argv1[0],argv1);
            }
            else
            {
                if(backrun == 0)
                {
                    waitpid(p1,NULL,0);
                }
            }
        }
        else//普通命令
        {
            index1 = 0;
            argv1[index1++] = command;
            for(i=0; i<comlen; i++) 
            {
                if(command[i] == ' ')
                {
                    command[i] = 0;
                    argv1[index1++] = &command[i+1];
                }
            }
            argv1[index1] = NULL;

            p1 = fork();
            if(p1 == 0)
            {
                execvp(argv1[0],argv1);
            }
            else
            {
                if(backrun == 0)
                {
                    waitpid(p1,NULL,0);
                }
            }
        }
    }
}

int main()
{
    char linebuf[MAXLEN], *command;
    int buflen,i;

    while(1)
    {
        write(STDOUT_FILENO,"$",1);
        fgets(linebuf,MAXLEN,stdin);
        buflen = strlen(linebuf);
        if(linebuf[buflen-1] == '\n')
        {
            linebuf[buflen-1] = 0;
            buflen--;
        }

        if(strcmp(linebuf,"exit") == 0)
            exit(0);
        else
        {
            command = linebuf;
            for(i=0; i<buflen; i++)
            {
                if(linebuf[i] == ';')
                {
                    linebuf[i] = 0;
                    runcommand(command); //循环执行多条命令
                    command = &linebuf[i+1];
                }
            }
            runcommand(command); //多条命令最后一条
        }
    }
    return 0;
}

5、程序的健壮性优化请参阅:
http://www.powerxing.com/unix-simple-shell-with-argv/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值