【Linux】:自定义shell(简易版)

朋友们、伙计们,我们又见面了,本期来给大家带来一期自定义shell,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

引言: 

1. 命令行提示符 

2. 获取用户指令

2.1 封装命令行和获取指令

3. 执行用户输入的命令

3.1 分割命令字符串

3.2 创建子进程执行指令

3.3  封装分割字符串和执行命令

4. 内建命令的执行

4.1 cd命令 

4.2 export命令

4.3 echo命令

5. 检查重定向

6. 完整代码


引言: 

Linux命令行的功能非常强大,我们用了这么长时间的shell,那么本期我们来自己实现一个简单版的shell。

1. 命令行提示符 

我们可以先看一下原本的shell:

要实现一个shell先得有一个命令行提示符,这个命令行提示符前面是用户名,中间是主机号,后面是工作目录,这些属性我们可以直接定义,但是在Linux环境变量中都保存着这些属性,并且是动态的,所以我们直接使用环境变量中的这些属性;

在程序中获取环境变量的接口叫做getenv:

#include <stdlib.h>
char *getenv(const char *name);

接下来我们先使用getenv实现三个获取命令行提示符的函数:

// 获取用户名
const char* getUsername()
{
    const char* name = getenv("USER");
    if(name) return name;
    return "none";
}
// 获取主机名
const char* getHostname()
{
    const char* hostname = getenv("HOSTNAME");
    if(hostname) return hostname;
    return "none";
}
// 获取当前工作目录
const char* getCwd()
{
    const char* cwd = getenv("PWD");
    if(cwd) return cwd;
    return "none";
}

获取完成之后,紧接着按照原本shell的格式来把他们拼接在一起打印出来看一看:

int main()
{
    // 打印命令行提示符
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    return 0;
}

可以看到我们的shell命令行提示符已经完成了,接下来就需要获取用户输入的命令了。

2. 获取用户指令

要获取用户输入的指令,我们先定义一个用来保存指令的字符数组,由于我们输入的指令可能不只是单纯的一个,还会输入一些带选项的指令,此时就不能使用scanf来读取,需要用按行读取的函数接口,我们选择fgets:

C语言会默认打开三个流:

  • ① 标准输入流:stdin
  • ② 标准输出流:stdout
  • ③ 标准错误流:stderr

所以我们从标准输入流中读取用户写入的命令:

#define NUM 1024

int main()
{
    char usercommand[NUM];

    // 打印命令行提示符
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    // 获取用户输入命令
    char* r = fgets(usercommand, sizeof(usercommand), stdin);
    if(r == NULL) return 1;
    
    return 0;
}

当我们输入命令时,最后肯定会跟上一个'\n',所以我们需要将命令行中的最后一个位置的'\n'设置为'\0'。

int main()
{
    char usercommand[NUM];

    // 打印命令行提示符
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    // 获取用户输入命令
    char* r = fgets(usercommand, sizeof(usercommand), stdin);
    if(r == NULL) return 1;
    // 命令规范化
    usercommand[strlen(usercommand) - 1] = '\0';   // 不会存在越界问题
    return 0;
}

这样写是不存在越界的问题,假设我们什么命令都不输入,但是最后还是会敲一下回车,所以只有一个'\n',将这个'\n'改为'\0'也符合预期。

2.1 封装命令行和获取指令

将打印命令行提示符和获取用户指令封装称为一个函数,后面我们直接调用这个函数即可。

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

#define NUM 1024

int getUserCommand(char *command, int sz)
{
    // 打印命令行提示符
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    // 获取用户输入命令
    char* r = fgets(command, sz, stdin);
    if(r == NULL) return -1;
    // 命令规范化
    command[strlen(command) - 1] = '\0';   // 不会存在越界问题
    return strlen(command);
}

int main()
{
    char usercommand[NUM];
    getUserCommand(usercommand, sizeof(usercommand));
    //printf("%s", usercommand);
    return 0;
}

3. 执行用户输入的命令

获取完用户输入的指令,接下来就需要执行对应的指令了。 

3.1 分割命令字符串

要能正确执行对应的命令,就需要将输入的命令以及选项做合理的分割:分割命令字符串我们选用C语言中的strtok函数

将分割好的字符串存放在一个字符指针数组中,最后以NULL结尾即可:

<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stackY、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值