UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell

一 分析

  要实现一个shell,需包含3个步骤
  1)读入指令
  2)指令解析
  3)执行指令

1 从键盘读入指令

  从键盘读入指令的几个要点:

  1)调用getc函数等待并获取用户键盘输入。
  2)每一行命令的结束符为' ',getsline函数就是通过这个结束符来判断用户是否完成指令的输入。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char* cmdLine = (char*)malloc(sizeof(char)*100);
    char* prompt = "print your cmd >";
    int i;

    while(1)
    {
        
        i = NextCmd(prompt,cmdLine);
        if(i != 0)
        {
            return i;
        }
        else
        {
            printf("you print a cmd: %s 
",cmdLine);
        }    
    }
    free(cmdLine);
    return 0;
}

int NextCmd(char* prompt,char* cmdLine)
{
    int i;
    printf("%s",prompt);
    i = GetsLine(cmdLine);
    if(i != 0)
    {
        return i;
    }
    else
    {
        return 0;
    }
}

int GetsLine(char* result)
{
    int word;

    while(1)
    {
        word = getc(stdin);
        if(word != '
')
        {
            *result = word;
            result ++;
        }
        else
        {
            *result = '';
            return 0;
        }
    }
}

 输出:

print your cmd >asd
you print a cmd: asd
print your cmd >a
you print a cmd: a
print your cmd >

  在上面的代码中我们从键盘上获得用户的指令输入,然后再直接打印出来。在GetsLine()函数执行getc(),等待用户输入并开始记录,直到用户输入回车后返回,返回前添加''字符表示整条指令结束。在NextCmd()中输出提示符“print your cmd >”,执行GetsLine获得用户输入的整条指令并返回。最后在主函数中打印用户的输入。

2 指令解析

  指令解析的几个要点:

  1)支持的指令格式【指令】 【参数1】 【参数2】。。。【参数n】
  2)指令与参数间,参数与参数间均以空格隔开,一条完整的指令以''结尾。
  3)因为使用函数execvp来执行指令(在后面会讲到),所以需要将解析的指令存入指针数组char* argList[]中,结尾处为NULL。

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


int main()
{
    char* argList[20];
    char* cmdLine = "abc -a -b -c";
    int i;
    for(i = 0;i<20;i++)
    {
        argList[i] = (char*)malloc(sizeof(char)*100);
        memset(argList[i],'',sizeof(char)*100);
    }

    CmdToArg(cmdLine,argList);

    for(i = 0;i<20;i++)
    {
        printf("get:%s
",argList[i]);
    }

    for(i = 0;i<20;i++)
    {
        free(argList[i]);
    }    
    return 0;

}

int CmdToArg(char* cmdLine,char* argList[20])
{
    char aChar;
    char* pChar;
    int i = 0;
    pChar = argList[0];
    while(1)
    {
        aChar = *cmdLine;
        cmdLine++;
        if(aChar == ' ')
        {
            *pChar = '';
            i++;
            pChar = argList[i];
        }
        else if(aChar == '')
        {
            *pChar = '';
            i++;
            argList[i] = 0; 
            return 0;
        }
        else
        {
            *pChar = aChar;
            pChar++;
        }
    }

}

  上述代码中,使用CmdToArg()函数,将cmdLine中的字符以空格符为间断符号解析,并存入一个argList中,且在argList的末尾处添加‘0’字符,表明一行指令的结束。

3 执行指令

  我们使用execvp()函数来启动另一个程序,这里有几点需要注意:

  1)新的程序会覆盖原程序,导致新程序结束时原本的shell也结束了,所以需要使用fork()函数来新建一个进程来打开新的程序。
  2)fork()函数的返回值可以判断当前进程是父进程还是子进程。
  3)在父进程中使用wait()函数来等待新进程结束

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

int main()
{
    char* arglist[3];
    int i;

    arglist[0] = "ls";
    arglist[1] = "-l";
    arglist[2] = 0;    

    i = fork();
    if(i==0)
    {
        printf("I'm the new process.ready for cmd ls
");
        execvp("ls",arglist);
    }
    else
    {
        wait(NULL);
        printf("I'm the old process
");
    }
}    

  在上面的代码中,我们使用fork()函数创建了一个新的进程,并在新进程中使用execvp()函数来启动新的程序,并在父进程中使用wait()函数来等待子进程结束。输出如下:

lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out
I'm the new process.ready for cmd ls
total 44
-rwxrwxr-x 1 lqx lqx 7268 2013-07-02 10:33 a.out
-rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
-rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
-rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
-rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
-rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
-rw-rw-r-- 1 lqx lqx  438 2013-07-02 10:33 test.c
-rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
-rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c
I'm the old process

4 整合

  在前面,我们分别实现了:
  1)从用户终端获得用户的指令输入
  2)将一行指令解析为指定格式
  3)创建子进程来执行用户的指令

  现在就将以上3各部分整合到一起,实现一个基本的shell。

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

int main()
{
    char* cmdLine = (char*)malloc(sizeof(char)*100);
    char* prompt = "print your cmd >";
    int i;
    char* argList[20];
    for(i = 0;i<20;i++)
    {
        argList[i] = (char*)malloc(sizeof(char)*100);
        memset(argList[i],'',sizeof(char)*100);
    }
    while(1)
    {
        
        i = NextCmd(prompt,cmdLine);
        if(i != 0)
        {
            return i;
        }
        else
        {
            CmdToArg(cmdLine,argList);
        i = fork();
        if(i==0)
            {
            execvp(argList[0],argList);
        exit(0);
            }
        else
        {
        wait(NULL);
        }
        }    
    }
    free(cmdLine);
    for(i = 0;i<20;i++)
    {
        free(argList[i]);
    }  
    return 0;
}

int NextCmd(char* prompt,char* cmdLine)
{
    int i;
    printf("%s",prompt);
    i = GetsLine(cmdLine);
    if(i != 0)
    {
        return i;
    }
    else
    {
        return 0;
    }
}

int GetsLine(char* result)
{
    int word;

    while(1)
    {
        word = getc(stdin);
        if(word != '
')
        {
            *result = word;
            result ++;
        }
        else
        {
            *result = '';
            return 0;
        }
    }
}

int CmdToArg(char* cmdLine,char* argList[20])
{
    char aChar;
    char* pChar;
    int i = 0;
    pChar = argList[0];
    while(1)
    {
        aChar = *cmdLine;
        cmdLine++;
        if(aChar == ' ')
        {
            *pChar = '';
            i++;
            pChar = argList[i];
        }
        else if(aChar == '')
        {
            *pChar = '';
            i++;
            argList[i] = 0; 
            return 0;
        }
        else
        {
            *pChar = aChar;
            pChar++;
        }
    }

}

  测试一下:

print your cmd >ls -l
total 48
-rw-rw-r-- 1 lqx lqx 1863 2013-07-02 10:52 
-rwxrwxr-x 1 lqx lqx 7491 2013-07-02 11:10 a.out
-rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
-rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
-rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
-rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
-rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
-rw-rw-r-- 1 lqx lqx 1842 2013-07-02 11:10 test.c
-rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
-rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c

  以上,我们实现了一个基本的shell,虽然还有很多不足之处,但是对shell的基本原理和功能都有了一些了解。

原文地址:https://www.cnblogs.com/cation/p/3045240.html