什么是shell
shell 翻译成中文就是壳,外壳的意思。它是一个命令行解释器,负责接收用户命令,然后调用相应的应用程序。它将用户和系统内核隔离开来,做他们两者间交互的桥梁。
这就是为什么我们发送一系列命令,系统都知道对应的操作是什么。
shell的功能
1.用户发送命令给shell:将使用者的命令翻译解释给中央内核处理
2.内核发送反馈结果给shell:将核心处理结果翻译呈现给使用者
举个例子:古代为了保证闺中少女的名节,保护少女们收到伤害,和男子决定婚嫁沟通是由媒婆来完成之间交流等活动的。那我们这个shell是不是和媒婆很像呢。
shell这个命令行解释器是一个统称概念,具体到Linux中是有/bin/bash//根目录下bin目录中的bash完成的
模拟shell过程
我们先观察一下Linux下的shell是工作呈现的结果是什么吧。
以ls命令为例子:
我们的显示格式有命令提示符:[用户@主机名 目录] 以及我们的命令组成。
我们暂且忽略命令提示符,先完成命令部分。
1.首先利用vim一个文件
具体操作见博客linux vi/vim详解
vim myshell//进入
//从普通模式进入可写模式,然后开始写代码
2.从输入终端获取字符串,因为scanf(%s)不能接收空格,所以采用另一种格式接收整一行数据
2.分解字符串,按照空格位置拆分到一个一个的命令参数,分别存入数组的各个单元中去
3.创建一个子进程,在子进程中调用exec进行进程替换,让子进程去运行命令
关于exec函数,父子进程以及进程替换详细见博客linux 如何创建子进程fork和fork,vfork,exec函数组详细介绍
4.父进程阻塞式等待子进程运行完毕获取退出状态码,
详细参见博客,为什么父进程要等待。linux 进程等待和退出码
5.循环接收命名,重复操作,直至接收到中断信息。
具体代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int argc;//获取拆分后字符串个数
char* argv[32];//存储拆分后的字符串
//按照空格位置拆分大的字符串为小的字符串在逐一存入数组
int Spl_str(char *buff)
{
if(buff == NULL)
return -1;
char *ptr = buff;//利用ptr遍历buff字符串
char *pro_ptr = ptr;//pro_ptr记录每单个字符串开始字节位置
argc = 0;
while((*ptr) != '\0'&&*(ptr)!='\n')
{
//如果ptr遍历中遇到\n换行,或者\0字符串结束标志则遍历完成
if(*ptr == ' '&& *(ptr+1) != ' ')
{
//这里是避免出现空格后还是空格,及多个空格连续出现的情况。
//因为没有命令中含有空格,所以如果空格后还是空格,则继续找,直到找到非空格前最后一个空格才进行操作
*ptr = '\0';//这个空格同时也是上一个字符串的结束标志。
argv[argc] = pro_ptr;
//pro_ptr存储的是单个小字符串的开始位置,这句话则是将上一个刚刚确定结尾位置的字符串存入到argv数组中。
pro_ptr = ptr + 1;//记录下一个字符串的开始字符位置
argc++;//因为argc位置已经存了,则++,
}
ptr++;//ptr++继续向后走,遍历查找
}
if(*(pro_ptr)!='\0'&&*(pro_ptr)!='\n')
argv[argc++] = pro_ptr;
//这句话的意思,因为最后一位的标志不是空格,位置字符串结束。如果这时候pro_ptr就是小字符串的开始位置不是最后无用字符,而依然是字符命令,则接收。
argv[argc] = NULL;
//argv 数组在execvp传参要求标志末尾用一个NULL
return 0;
}
//先通过fork创建子进程,在通过父子进程不同的返回值,让子进程实现进程替换,
int exec_cmd()
{
int pid = -1;
pid = fork();
if(pid < 0)
{
return -1;
//创建失败
}
else if(pid == 0)
{
//pid==0证明在子进程
exit(execvp(argv[0],argv));
//调用只前创建的argv,argc,传参具体格式见博客
}
int status;
wait(&status);
//通过获取退出码,查看出现的错误,并且转换成为退出码所对应的错误信息并且打印。
if(WIFEXITED(status))
{
//利用系统提供的宏子进程的退出码,也可以用&操作拿到第7位,并转换为文本信息打印,可以不打印,不显示。
printf("%s\n",strerror(WEXITSTATUS(status)));
}
return 0;
}
//主函数:
int main()
{
while(1)
{
printf("[myshell]$ ");
fflush(stdout);//清空缓冲区,因为没有换行,需要人为清空缓冲区
{
char buff[1024]={0};
//scanf("%s",buff);
scanf("%32[^\n]",buff);
scanf("%*c");
//%[^\n]获取数据直到遇到\n为止
//%*c 清空缓冲区,接收\n并丢弃
Spl_str(buff);
}
exec_cmd();
}
return 0;
}
运行显示结果如下: