intro
- 争取在自己的简易shell中可以实现以下命令
xxx@xxx ~ $ ./xxx-super-shell
xxx@xxx ~ $ echo ABCDEF
xxx@xxx ~ $ echo ABCDEF > ./1.txt
xxx@xxx ~ $ cat 1.txt
xxx@xxx ~ $ ls -t >> 1.txt
xxx@xxx ~ $ ls -a -l | grep abc | wc -l > 2.txt
xxx@xxx ~ $ python < ./1.py | wc -c
xxx@xxx ~ $ mkdir test_dir
xxx@xxx ~/test_dir $ cd test_dir
xxx@xxx ~ $ cd -
xxx@xxx ~/test_dir $ cd -
xxx@xxx ~ $ ./xxx-super-shell # shell 中嵌套 shell
xxx@xxx ~ $ exit
xxx@xxx ~ $ exit
任务解析
-
任务目标:
- 实现
管道
(也就是 |) - 实现
输入输出重定向
(也就是 < > >>) - 实现
后台运行
(也就是 & ) - 实现
cd
,要求支持能切换到 绝对路径,相对路径和支持cd -
屏蔽
一些信号(如 ctrl + c 不能终止)- 界面
美观
- 不得出现内存泄漏,内存越界等错误
- 实现
-
核心为掌握
Linux系统编程
中进程
的相关部分,能够正确调用相应API完成任务,保证每个函数的逻辑正确 -
难点:
- 最难的就是实现管道,而且是多重管道ಥʖ̯ಥ,关于实现管道的的基本原理可以参考我的这篇博客——Linux下实现一个简单的单向管道及其理解
- 代码的
去耦合
与功能分块
也是一个难点,但这次做的还行 - 写完之后再回想,想不起来很多当时觉得难的不行的东西了,其实经历完绝望之谷之后,不仅技术会提升,心态也会更好
框架函数
main()
- 从main函数入手来剖析我们需要实现的所有功能是一个不错的选择
int main()
{
my_signal(); //屏蔽信号
while(1){
//一直循环,直至手动退出
char place[BUFFSIZE]; //存放当前路径
getcwd(place, BUFFSIZE); //获取当前路径
printf(BEGIN(36,36)"%s:"CLOSE, place); //色彩显示
// readline库的使用
char *command = readline(BEGIN(33,33)"ypd-super-shell ¥$ "CLOSE);
if(!command){
my_error("readline",__LINE__);
}
add_history(command); //存放历史命令
write_history(NULL); //写入历史命令
parse(command); //解析命令
do_cmd(argc,argv); //执行命令
argc = 0; //将argc置0,重新读取命令
free(command); //释放堆区空间,等待重新分配(由readline库函数完成)
}
}
-
在main函数中,我们首先会注意到
readline
这个完全陌生的库,它帮助我们完成了很多工作:- 用户命令的获取
- 动态内存的申请
- 历史命令的存写
- 那么,我们怎么才能掌握这么有用的库呢,这个问题的答案碍于篇幅我就不再展开,请大家参考这篇博客——readline库的简单使用
-
parse()
: 顾名思义,这个函数就是用来解析命令的 -
do_cmd()
:核心函数,执行用户输入的命令
parse(command)
- 解析
command
SHOW ME THE CODE:
void parse(char *command)
{
/*
command 为用户输入的命令
*/
//初始化argv与argc
for(int i = 0; i < MAX_CMD; i++){
argv[i] = NULL;
for(int j = 0;j < MAX_CMD_LEN; j++){
COMMAND[i][j] = '\0';
}
}
argc = 0;//命令数计数器
memset(backupCommand,0,sizeof(backupCommand));//非常重要,因为漏了这一句被整自闭了好久
strcpy(backupCommand, command);//备份命令
int j = 0;
int len = strlen(command);
for(int i = 0; i < len; i++){
if(command[i] != ' '){
COMMAND[argc][j++] = command[i];
}else{
//command[i] == ' '
if(j != 0){
//j为0则为连续空格情况
COMMAND[argc][j] = '\0';
argc++;
j = 0;
}
}
}
if(j != 0){
//处理命令行末尾
COMMAND[argc][j] = '\0';
}
/*处理__内置命令__ | isspace()调用是为了处理空格*/
argc = 0;
int flg = OUT;
for(int i = 0; command[i] != '\0'; i++){
if(flg == OUT && !isspace(command[i])){
flg = IN;
argv[argc++] = command + i;
}else if(flg == IN && isspace(command[i])){
flg = OUT