文章目录
1.模拟实现shell
1.1思路
1.我们的shell需要展示当前用户、主机、当前目录、等命令行提示符
我们知道,shell中命令行提示符一直在显示的,因此需要一个while循环来控制,即每次进程开始都需要打印这一串字符串
2.需要能够获取命令行
获取命令,可以建立一个字符数来存储命令行,通过fgets来从标准输入获取
需要注意的是fgets会将最后一个换行符带入字符数组中,这里需要进行处理
同时通过strtok将输入的字符串分解成标准的命令模式,使用strtok可以避免自己去处理多个连续空格的问题
3.需要能够解析执行命令行
由于命令行是可以持续输入的,因此我们将命令行解析之后,需要创建子进程来进行命令行的执行
通过调用替换函数来完成命令行的解析执行工作
4.需要获取命令实现状态
父进程通过waitpid以阻塞的方式来获取子进程的执行状态
1.2实现代码、效果展示
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SIZE 128
void Redirect(char *str)
{
int count=0;//计数器
char *file=NULL;//保存文件名
while(*str)//不为\0
{
if(*str=='>')
{
*str='\0';
str++;//往后挪动一步
count++;
if(*str=='>')//追加重定向
{
*str='\0';//将>重置为\0,避免对后面的命令解析产生干扰
str++;
count++;
}
//文件名获取
while(*str!='\0'&&*str==' ')//是空格,则往后移动,找到文件名为止
{
str++;
}
file=str;//得到文件名
while(*str!='\0'&&*str!=' ')//防止后面带多个空格
{
str++;
}
*str='\0';
//重定向
int fd=0;
if(1==count)
{
fd=open(file,O_CREAT |O_TRUNC | O_WRONLY,0644);
}
else if(2==count)
{
fd=open(file,O_CREAT | O_WRONLY | O_APPEND,0644);
}
dup2(fd,1);
close(fd);
}
else if(*str=='<')
{
*str='\0';
str++;//往后挪动一步
//文件名获取
while(*str!='\0'&&*str==' ')//是空格,则往后移动,找到文件名为止
{
str++;
}
file=str;//得到文件名
while(*str!='\0'&&*str!=' ')//防止后面带多个空格
{
str++;
}
*str='\0';
//重定向
int fd=open(file,O_RDONLY);
dup2(fd,0);
close(fd);
}
else
str++;
}
}
int main()
{
char str[SIZE]={0};
while(1)
{
printf("[myshell@VM-0-6-centos Shell]* ");//命令行提示
str[0]=0;//字符数组清空,字符串/0结尾,因此第一个值为0就是清空
fgets(str,SIZE,stdin);//从标准输入获取字符串
str[strlen(str)-1]='\0';//fgets会将\n带入
char *args[SIZE]={0};//将命令解析出来
pid_t id=fork();
if(id<0)
{
perror("fork error\n");
}
if(id==0)//child
{
Redirect(str);
args[0]=strtok(str," ");//以空格为分隔符解析出来
int sub=1;
do
{
args[sub]=strtok(NULL," ");
if(!args[sub])
break;//解析完毕
sub++;
}while(1);
execvp(args[0],args);
//调用失败
printf("comman not found\n");
exit(1);
}
//父进程不用判定,子进程调用替换函数后就被替换掉了
int st;
waitpid(id,&st,0);//以阻塞的方式等待;
printf("status_code:%d\n",st>>8&&0xff);//打印子进程状态码
}
return 0;
}
1.3应用迷你shell
1.4总结
shell的本质是一个进程,这个进程在获取指令,分析指令,创建子进程执行指令
我们登录shell经过验证之后,系统就给我们创建了一个对应的进程,输出命令行提示符
经过迷你shell 的实现,可以清楚的知道,我们的进程可以fork另外一个进程,并且给他一些参数,被调用的进程来执行一些任务,该进程执行失败与否不会影响到父进程,并且父进程可以获取到子进程执行任务的状态
我们的shell本质就是这样,通过创建子进程来完成执行任务,假使任务失败或者输入无效的命令也不会导致父进程的奔溃
2.系统文件I/O接口介绍
2.1为什么默认打开三个输入输出流
不论是什么语言都会默认打开三个输入输出流,即stdin(默认输入、对应键盘),stdout(默认输出、对应显示器),stderr(标准错误、对应显示器)
首先我们的语言是人和计算机交互的工具,交互就需要有信息的传递,而三个输入输出流就是交互的最低保证,我们通过键盘输入信息给计算机,计算机通过显示器将信息传送给操作者,完成信息的交互工作
2.2接口介绍
所有的I/O操作都是和硬件进行交互,硬件是不能直接进行操控的,我们需要通过操作系统来操控硬件,方式是通过调用操作系统提供的接口来进行调用
因此一些语言的I/O操作库函数其实就是以系统调用接口为基础进行封装而成的,呈现出上下级的形式。比如我们上面调用的都是库函数,这些函数都是在系统调用的基础上进行封装而成,接下来,介绍一下系统I/O的一些基本接口
3.文件描述符fd
3.1文件描述符是什么
文件描述符就是调用open时候的返回值,如果创建成功这个返回值大于零,如果创建失败则返回-1
3.2文件描述符分配规则
对进程来讲默认从3开始分配描述符,因为有三个默认打开的标准输入输出流,如果关闭了,会从最小的没有被分配的开始分配
3.3如何理解Linux下一切皆文件
3.4进程是怎样找到文件的
3.5扩展——面向对象语言的产生
4.重定向
4.1什么是重定向
把本应该显示到显示器上面的内容,写到了文件里面这种就叫做文件重定向
> 跟的是文件名,子进程先把1关掉,再将文件打开,然后写入内容