[Linux入门]---使用exec函数实现简易shell

1.简易实现

2.人机交互,获取命令行

代码如下:

int quit=0;
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024

char pwd[LINE_SIZE];
char commandline[LINE_SIZE];

const char* getusername()
{
    return getenv("USER");
}

void getpwd()
{
    getcwd(pwd, sizeof(pwd));
}
void interact(char* cline, int size)
{
    //获取主机名
    char hostname[256];
    gethostname(hostname, sizeof(hostname));
    //获取当前路径
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(),hostname, pwd);
    char* s = fgets(cline, size, stdin);
    assert(s);//debug版本下assert才起效果
    //release,assert失效
    (void)s;//使用一下,可以在不同版本都生效,抵挡编译器报警

    //"abcd\n\0"
    cline[strlen(cline)-1] = '\0';
}
int main()
{
	while(!quit)
	{
	    //交互问题,获取命令行
    	interact(commandline,sizeof(commandline));
    	printf("%s\n",commandline );
    }
    return 0;
}

代码运行的的结果为:
在这里插入图片描述
运行结果如上,程序获取命令行以及提示行显示的功能已经完成了。

3.命令行分割

代码如下:

#define DELIM " "
#define ARGC_SIZE 32

char* argv[ARGC_SIZE];


int splitstring(char cline[], char* _argv[])
{
    int i = 0;
    _argv[i++] = strtok(cline, DELIM);
    //stork函数扫描字符串末尾时,返回空指针
    while(_argv[i++] = strtok(NULL,DELIM));//故意写的=

    return i - 1;
}
int main()
{
    while(!quit)
    {
        //交互问题,获取命令行
        interact(commandline,sizeof(commandline));

        //3.子串分割问题,解析命令行
        
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        
        for(int i = 0; i < argc; i++)
        {
            printf("%s\t",argv[i]);
        }
        
        printf("\n");
    }
    return 0;
}

代码运行的的结果为:
在这里插入图片描述
运行结果如上,命令行分割成字符串并存放进入数组使用。

4.执行命令

代码如下:

#define EXIT_CODE 44

int lastcode = 0;

void NormalExcute(char* _argv[])
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //让子进程执行命令
        execvp(_argv[0],_argv);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id, &status,0);
        if(rid == id)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}
int main()
{
    while(!quit)
    {
        //1.交互问题,获取命令行
        interact(commandline,sizeof(commandline));

        //2.子串分割问题,解析命令行
        
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        

        for(int i = 0; i < argc; i++)
        {
            //printf("%d:",i);
            //printf("%s\n",argv[0]);
            printf("%s\n",argv[i]);
        }
        
        printf("\n");
        
        
        //3.普通命令的执行
        //if(!n)
        NormalExcute(argv);
    }
    return 0;
}

代码运行的结果:
在这里插入图片描述
运行结果如上,可以让子进程执行替换函数,调用系统程序命令执行!

5.内建命令

在这里插入图片描述
我们发现使用一些命令的时候没有结果,诸如“”cd\echo”等命令,因为这些命令属于内建命令,是要父进程执行的。
代码如下:

#define EXIT_CODE 44


int lastcode = 0;

//自定义环境变量表
char myenv[LINE_SIZE];
//自定义本地变量表

int buildCommand(char* _argv[], int _argc)
{
    if(_argc == 2 && strcmp(_argv[0], "cd") == 0)
    {
        //修改父进程中的当前路径
        chdir(argv[1]);
        //把修改路径放到pwd数组中
        getpwd();
        //把pwd数组的路径,放到环境变量中
        sprintf(getenv("PWD"), "%s", pwd);
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "export") == 0)
    {
        strcpy(myenv, _argv[1]);
        putenv(myenv);//增加环境变量到父进程中
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
    {
        if(strcmp(_argv[1],"$?") == 0)
        {
            printf("%d\n", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$')
        {
            char* val = getenv(_argv[1]+1);
            if(val) printf("%s\n", val);
        }
        return 1;
    }

    //特殊处理一下
    if(strcmp(_argv[0], "ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }

    return 0;
}
int main()
{
    while(!quit)
    {
        //1.交互问题,获取命令行
        interact(commandline,sizeof(commandline));

        //2.子串分割问题,解析命令行
        
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        

        for(int i = 0; i < argc; i++)
        {
            //printf("%d:",i);
            //printf("%s\n",argv[0]);
            printf("%s\n",argv[i]);
        }
        
        printf("\n");
        
        //指令的判断
        //内建命令,本质就是一个shell内部的一个函数
        int n = buildCommand(argv, argc);
        
        //3.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

代码运行的结果如下:
在这里插入图片描述
在执行普通命令之前,我们需要判断是不是内建命令,如果是的话,分析之后让父进程执行!

6.myshell代码

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

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
#define DELIM " "
#define ARGC_SIZE 32
#define EXIT_CODE 44

int quit=0;
char pwd[LINE_SIZE];
char commandline[LINE_SIZE];
char* argv[ARGC_SIZE];
int lastcode = 0;

//自定义环境变量表
char myenv[LINE_SIZE];
//自定义本地变量表

const char* getusername()
{
    return getenv("USER");
}
//const char* gethostname()
//{
//    return getenv("HOSTNAME");
//}
const char* mygethostname()
{
    //char myhostname[LINE_SIZE];

    return getenv("HOSTNAME");
}
void getpwd()
{
    getcwd(pwd, sizeof(pwd));
}
void interact(char* cline, int size)
{
    //获取主机名
    char hostname[256];
    gethostname(hostname, sizeof(hostname));
    //获取当前路径
    getpwd();
    //printf("%s\n",mygethostname());
    //printf(LEFT"%s@%s%s"RIGHT""LABLE" ",getusername(),mygethostname(),pwd);
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(),hostname, pwd);
    char* s = fgets(cline, size, stdin);
    assert(s);//debug版本下assert才起效果
    //release,assert失效
    (void)s;//使用一下,可以在不同版本都生效,抵挡编译器报警

    //"abcd\n\0"
    cline[strlen(cline)-1] = '\0';
}

int splitstring(char cline[], char* _argv[])
{
    int i = 0;
    _argv[i++] = strtok(cline, DELIM);
    //stork函数扫描字符串末尾时,返回空指针
    while(_argv[i++] = strtok(NULL,DELIM));//故意写的=

    return i - 1;
}

void NormalExcute(char* _argv[])
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //让子进程执行命令
        execvp(_argv[0],_argv);
        exit(EXIT_CODE);
    }
    else
    {
        //创建子进程失败
        int status = 0;
        pid_t rid = waitpid(id, &status,0);
        if(rid == id)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}
int buildCommand(char* _argv[], int _argc)
{
    if(_argc == 2 && strcmp(_argv[0], "cd") == 0)
    {
        //修改父进程中的当前路径
        chdir(argv[1]);
        //把修改路径放到pwd数组中
        getpwd();
        //把pwd数组的路径,放到环境变量中
        sprintf(getenv("PWD"), "%s", pwd);
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "export") == 0)
    {
        strcpy(myenv, _argv[1]);
        putenv(myenv);//增加环境变量到父进程中
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
    {
        if(strcmp(_argv[1],"$?") == 0)
        {
            printf("%d\n", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$')
        {
            char* val = getenv(_argv[1]+1);
            if(val) printf("%s\n", val);
        }
        return 1;
    }

    //特殊处理一下
    if(strcmp(_argv[0], "ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }

    return 0;
}
int main()
{
    while(!quit)
    {
        //1.交互问题,获取命令行
        interact(commandline,sizeof(commandline));

        //2.子串分割问题,解析命令行
        
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        
        
        //指令的判断
        //内建命令,本质就是一个shell内部的一个函数
        int n = buildCommand(argv, argc);
        
        //3.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值