操作系统实验: Linux命令解释程序设计与实现
探索、理解并掌握操作系统命令解释器的设计原理和实现机制,基于Linux内核进行相应命令解释程序的设计和实现,并在Linux操作系统平台上加以测试验证。
具体实验内容如下:
分析、设计与实现基于Linux内核的命令解释程序(Shell),主要包括系统环境变量的设置和初始化、系统命令提示符显示、命令辨别解析(区分内部命令与外部命令及不同内部命令)、典型内部命令(譬如显示指定目录下文件列表、显示文本文件内容、文件拷贝、文件删除、空文件创建、日期设置/显示)处理等功能,并在Linux操作系统上测试验证。
Linux命令解释程序功能设计要求:
环境名称 | 下载方式 |
---|---|
VMware Workstation 16 Pro | https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html |
ubuntu 20.04.4LTS (64-bit) | https://ubuntu.com/download/desktop |
GCC编译器 | 终端输入命令sudo apt-get install build-essential |
VSCode | 直接在ubuntu中的软件商店里下载 |
编程语言 : C语言
选择C语言是因为在linux下有很多系统调用函数可供C语言直接使用,方便实现相关的bash命令。除此之外,我比较熟悉C语言,上手较为容易。
功能设计:
(1)内建命令:
命令名 | 功能 | 参数个数 |
---|---|---|
help | 显示用户手册 | 0 |
environ | 显示所有的环境变量 | 0 |
time | 显示当前时间 | 0 |
pwd | 显示当前路径 | 0 |
clr | 清空屏幕内容 | 0 |
exit | 退出当前进程 | 0 |
quit | 退出自制的shell | 0 |
ls | 无参数则显示当前目录下的内容,有参数则显示参数所指目录下的内容 | 0、1 |
cd | 无参数则显示当前目录,有参数则改变当前目录为参数内容 | 0、1 |
exec | 使用参数代表的命令替换当前进程 | 1 |
unset | 将参数所指的环境变量的值取消 | 1 |
touch | 创建名为该输入参数的文件 | 1 |
set | 无参数时,显示所有环境变量;有2个参数时,设置第1个参数代表的环境变量的值为第2个参数 | 0、2 |
echo | 无参数则显示空内容,有参数则显示参数内容 | 0、+ |
copy | 将第一个参数文件的内容复制到第二个参数文件中 | 2 |
(2)外部命令:
非内部命令的命令为外部命令,拟设计shell 程序可以自动查找并执行外部命令。
(3)执行脚本文件:
如果不加参数的时候,则进入命令行输入模式,否则从.sh的脚本文件中批量执行命令。
(4)系统环境变量:
拟设计可以显示并修改系统的环境变量。
(5)I/O重定向:
拟设计支持I/O重定向的功能,在输入要执行的命令后,输入‘<’,再接输入重定向到的文件inputfile(从inputfile中读取而非从标准输入中读取);输入‘>’或者‘>>’再接输出重定向到的文件outputfile,myshell就会将命令执行的结果输出到outputfile中而非输出到屏幕上,其中‘>’表示覆盖写,‘>>’表示追加写
考虑到实际的bash总是在不停的等待用户的输入,用户输入后回车,然后执行相应的操作后又等待用户的下一次的输入。因此整体上是一个无限循环过程,mian()
函数可以用while(1){...}
的结构来模拟这个无限循环的过程。每一次循环主要包括如下几步:
用户名
@主机名
:当前目录
$”
,换行符\n
,制表符\t
。命令拆分后,可以得到各自的命令参数和数目,并可以据此判断其是否为特殊命令,如管道命令、重定向命令。实现函数:Help()
实现原理:打印用户手册
void Help()
{
printf("*********************************************************************")
printf("欢迎查看zyw_shell的用户手册 (^_^)/ \n");
printf("Author:ZhangYuwei | Date:2022/3/10 | Email:[email protected] \n\n");
printf("\n\n(1)内建命令的使用\n");
printf("+-------------------------------------------------------------------+\n\n");
printf("【cd】\n");
printf("解释: 无参数则显示当前目录,有参数则改变当前目录为参数内容\n");
printf("示例: cd 或 cd /home/zyw \n\n");
printf("【clr】\n");
printf("解释: 清空当前屏幕内容,无参数\n");
printf("示例: cd 或 cd /home/zyw \n\n");
printf("【echo】\n");
printf("解释: 无参数则显示空内容,有参数则显示参数内容\n");
printf("示例: echo 或者 echo hello\n\n");
printf("【exec】\n");
printf("解释: 使用参数代表的命令替换当前进程\n");
printf("示例: exec ls\n\n");
printf("【exit】\n");
printf("解释: 退出当前进程,无参数\n");
printf("示例: exit\n\n");
printf("【environ】\n");
printf("解释: 显示所有的环境变量,无参数\n");
printf("示例: environ\n\n");
printf("【cpoy】\n");
printf("解释: 文件复制,要有两个参数\n");
printf("示例: copy a.txt b.txt\n\n");
printf("【help】\n");
printf("解释: 显示用户手册,无参数\n");
printf("示例: help\n\n");
printf("【pwd】\n");
printf("解释: 显示当前路径,无参数\n");
printf("示例: pwd\n\n");
printf("【quit】\n");
printf("解释: 退出zyw_shell,无参数\n");
printf("示例: quit\n\n");
printf("【touch】\n");
printf("解释: 创建文件,有一个参数\n");
printf("示例: touch a.txt\n\n");
printf("【set】\n");
printf("解释: 无参数时,显示所有环境变量;有2个参数时,设置第1个参数代表的环境变量的值为第2个参数\n");
printf("示例: set 或 set USER Zhang\n\n");
printf("【time】\n");
printf("解释: 显示当前时间,无参数\n");
printf("示例: time\n\n");
printf("【unset】\n");
printf("解释: 将参数所指的环境变量的值取消,只有一个参数\n");
printf("示例: unset USER\n\n");
printf("\n\n(2)外部命令\n");
printf("+-------------------------------------------------------------------+\n\n");
printf("解释: 除了内建命令之外,zyw_shell还能够自动查找并执行外部命令\n");
printf("示例: ls -l 或 vi hadoop.txt\n\n");
printf("\n\n(3)脚本文件的执行\n");
printf("+-------------------------------------------------------------------+\n\n");
printf("解释: zyw_shell能够从脚本文件中提取命令行输入,在调用zyw_shell时,如果不加参数则进入命令行输入模式,如果加上一个脚本文件的参数,则会从参数代表的文件中提取命令并执行\n\n");
printf("示例: zyw_shell test.sh\n\n");
printf("\n\n(4)系统环境\n");
printf("+-------------------------------------------------------------------+\n\n");
printf("解释: zyw_shell可以显示并修改系统的环境变量。\n\n");
printf("\n\n(5)I/O重定向\n");
printf("+-------------------------------------------------------------------+\n\n");
printf("解释: zyw_shell支持I/O重定向的功能,在输入要执行的命令后,输入‘<’,再接输入重定向到的文件inputfile(从inputfile中读取而非从标准输入中读取);输入‘>’或者‘>>’再接输出重定向到的文件outputfile,zyw_shell就会将命令执行的结果输出到outputfile中而非输出到屏幕上,其中‘>’表示覆盖写,‘>>’表示追加写\n\n");
printf("示例: wc < hadoop1.txt >> hadoop2.txt\n\n");
printf("*****************************END************************************\n")
}
实现函数:Environ()
函数功能:打印所有的环境变量
实现原理:遍历指针数组environ
并打印。每个数组元素是指向环境变量字符串的指针。
void Environ()
{
extern char ** environ;
for(int i=0;environ[i]!=NULL;i++)
printf("%s\n",environ[i]);
}
实现函数:Add_environ
函数功能:添加环境变量
实现原理:直接调用putenv()函数即可添加
void Add_environ(char* str)
{
putenv(str); // 添加环境变量
}
实现函数:Cd()
函数功能:切换目录
实现原理:通过getcwd()
函数获取当前目录并存储到old_path中,通过chdir()
函数实现目录切换
void Cd(int num, int id)
{
char old_path[MAX_PATH_LENGTH];
getcwd(old_path,MAX_PATH_LENGTH);
if(num == 1){ //无参数
printf("%s\n",old_path); //输出当前目录
}
else if(num > 2) // 参数个数大于2
fprintf(stderr, RED "[zyw_shell] Error: 输入参数过多!\n");
else{//一个参数
//如果切换到该参数所指的目录成功,则修改PWD为新的当前目录
if(!chdir(commands[id+1]))
setenv("PWD",commands[id+1],1);
//否则说明路径不存在
else
fprintf(stderr, RED "[zyw_shell] Error: 找不到名为%s的路径!\n",commands[id+1]);
}
}
实现函数:Clr()
函数功能:清屏
实现原理:直接通过printf实现清屏,其中参数clear为宏定义。
#define CLEAR "\e[1;1H\e[2J"
void Clr()
{
printf(CLEAR);
}
实现函数:Copy()
函数功能:将参数一指示的文件复制到参数二指示的文件中
实现原理:利用c语言中文件的读写操作
void Copy(int num,int id) {
int from_fd,to_fd;
int bytes_read,bytes_write;
char buffer[MAX_INBUF_SIZE];
char*ptr;
if(num==3){
//打开原始文件
if((from_fd=open(commands[id+1],O_RDONLY))==-1)
{
fprintf(stderr, RED "[zyw_shell] Error:无法打开文件%s!\n",commands[id+1]);
return;
}
printf("正在打开 %s...\n",commands[id+1]);
//创建目标文件
if((to_fd=open(commands[id+2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr, RED "[zyw_shell] Error:无法打开文件%s!\n",commands[id+2]);
return;
}
printf("正在复制到 %s...\n",commands[id+2]);
//classical codes of copy file
while(bytes_read=read(from_fd,buffer,MAX_INBUF_SIZE))
{
//error
if(bytes_read==-1){
break;
}
else if (bytes_read>0)
{
ptr=buffer;
while(bytes_write=write(to_fd,ptr,bytes_read))
{
//error
if(bytes_write==-1) break;
//all read btyes
else if(bytes_write==bytes_read) break;
//continue writing
else if(bytes_write>0)
{
ptr+=bytes_write;
bytes_read-=bytes_write;
}
}
//erro when write
if(bytes_write==-1) break;
}
}
close(from_fd);
close(to_fd);
printf(YELLOW "拷贝成功!\n");
}
else{
fprintf(stderr, RED "[zyw_shell] Error: 输入参数只能有两个!\n");
}
}
实现函数:Pwd()
函数功能:显示当前目录
实现原理:通过getcwd()
函数获取当前目录并存储到now_path
中, 输出now_path
的内容到屏幕
void Pwd()
{
char now_path[MAX_PATH_LENGTH];
getcwd(now_path,MAX_PATH_LENGTH);
printf("%s\n",now_path);
}
实现函数:Exit()
函数功能:正常退出当前进程
实现原理:直接调用stdlib.h库中的exit()函数即可
void Exit()
{
exit(0);
}
实现函数:Time()
函数功能:显示当前时间
实现原理:time()
函数返回从公元1979年1月1日的UTC时间从0时0分0秒起到现在经过的秒数。locattime()
函数将参数所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法,然后将此结果由结构体tm返回。
void Time()
{
//定义类型为time_t的变量nowtime
time_t nowtime;
//定义指向tm结构的指针t
struct tm *t;
char* xingqi[] = {"星期天","星期一","星期二","星期三","星期四","星期五","星期六"};
time(&nowtime);
t = localtime(&nowtime);
//输出当前时间,格式为:年/月/日 星期X 时:分:秒
printf("%d年%d月%d日 %s %02d:%02d:%02d\n",(1900+t->tm_year),(1+t->tm_mon),t->tm_mday,xingqi[t->tm_wday],t->tm_hour,t->tm_min,t->tm_sec);
}
实现函数:Touch()
函数功能:创建一个新文件
实现原理:利用c语言中的文件写操作进行创建。
void Touch(int num, int id){
if(num==2){
FILE *file=fopen(commands[id+1],"w");
fclose(file);
printf(YELLOW "成功创建文件%s\n",commands[id+1]);
}
else{
fprintf(stderr, RED "[zyw_shell] Error: 输入参数只能有一个!\n");
}
}
实现函数:echo()
函数功能:在屏幕上显示内容并换行
实现原理:直接循环输出保存的参数,最后打印一个换行符即可
void Echo(int num, int id)
{
for(int i = 1;i < num;i++)
printf("%s ",commands[id + i]);
printf("\n");
}
实现函数:Exec()
函数功能:使用指定命令替换当前的shell
实现原理:不创建子进程,直接调用execvp()函数执行命令替换当前shell,如果execvp()返回值不为0,说明没有找到命令,输出错误信息
void Exec(int num, int id)
{
if(num == 1)
fprintf(stderr, RED "[zyw_shell] Error: 该命令需要一个参数!\n");
else{
char ** commands_for_exec = &(commands[id + 1]); //commands[id + 1] 指向待执行的命令
if(execvp(commands[id + 1],commands_for_exec)!=0)
fprintf(stderr, RED "[zyw_shell] Error: 无法找到这个命令!\n");
}
}
实现函数:Set()
函数功能:设置环境变量的值,没有参数则列出所有环境变量
实现原理:没有输入参数,则执行Environ_func()函数列出所有环境变量的值;输入了两个参数,第一个为变量名,第二个为变量值,如果没有找到输入的环境变量,则输出错误信息,找到了则设置该环境变量的值 。
void Set(int num, int id)
{
if(num == 1)
Environ();
else if(num == 3){
char* env = getenv(commands[id + 1]);
if(!env)
fprintf(stderr, RED "[zyw_shell] Error: 没有该环境变量!\n");
else
setenv(commands[id + 1],commands[id + 2],1);
}
//参数数量不对,则输出错误信息
else
fprintf(stderr, RED "[zyw_shell] Error: 参数数量只能是0个或者两个!\n");
}
void Environ() // 显示环境变量
{
extern char ** environ;
for(int i=0;environ[i]!=NULL;i++)
printf("%s\n",environ[i]);
}
void Add_environ(char* str)
{
putenv(str); // 添加环境变量
}
实现函数:Unset()
函数功能:删除环境变量
实现原理:输入1个参数为将要取消的环境变量,如果没有找到输入的环境变量,则输出错误信息,找到了则设置该环境变量的值为空字符串。否则参数数量不对,则输出错误信息
void Unset(int num, int id)
{
if(num == 2){
char* env = getenv(commands[id + 1]);
if(!env)
fprintf(stderr, RED "[zyw_shell] Error: 找不到环境变量!\n");
else
setenv(commands[id + 1],"",1);
}
else
fprintf(stderr, RED "[zyw_shell] Error: 参数数量只能为1个!\n");
}
实现函数:DisplayInfo()
函数功能:该函数用来显示命令提示符,包含当前路径,用户名和主机名
实现原理:通过getccwd()
获得当前路径存储在path中, getuid()
函数获取用户id; getpwuid()
函数根据用户id获取当前路径,保存到pwd中; 通过gethostname()
获得当前主机名保存到hostname中
void DisplayInfo()
{
char path[MAX_PATH_LENGTH]; //当前路径
char usrname[MAX_USRNAME_LENGTH];//用户名
char hostname[MAX_HOSTNAME_LENGTH];//主机名
getcwd(path,MAX_PATH_LENGTH);
struct passwd *pwd; //声明结构变量pwd来保存含有用户名信息的passwd结构
pwd = getpwuid(getuid());
strcpy(usrname,pwd->pw_name);
gethostname(hostname,MAX_HOSTNAME_LENGTH);
printf(YELLOW "[zyw_shell]%s@%s", usrname,hostname);
printf(WHITE ":");
printf(CYAN "%s", path);
printf(WHITE "$ ");
}
实现函数:Read_command()
函数功能:读取用户输入的命令以及参数并存储到全局变量inbuf中
实现原理:首先设置一个脚本指针script_fp
,如果传入的参数script_fp为NULL,则从标准输入流读取MAX_INBUF_SIZE-1个字符到inbuf中。否则从参数所指的脚本文件中读取。
void Read_command(FILE* script_fp)
{
if(!script_fp)
fgets(inbuf,MAX_INBUF_SIZE-1,stdin);
else{
if(fgets(inbuf,MAX_INBUF_SIZE-1,script_fp) == NULL){
fclose(script_fp);
exit(0);
}
}
}
实现函数:Split_command()
函数功能:分割读取的输入并存储到全局变量commands中
实现原理:定义分隔符为空格,换行符和制表符,利用strtok()函数将inbuf以分隔符delim进行分割,第一次分割的结果存放在temp中。如果temp为NULL,说明用户没有输入或者输入均为分隔符,返回1,temp不为NULL,则将temp赋值给commands[0]。在command_count不超过MAX_COMMAND_NUM,且继续分割的结果字符串不为NULL时,将command_count每次递增1。用两个整数needcpy_infile
和needcpy_outfile
分别指示是否需要拷贝, 需要输入重定向和输出重定向文件的路径。如果needcpy_infile为1,则将当前分割结果复制到infile_path中并重新设置needcpy_infile为0,如果needcpy_outfile为1,则将当前分割结果复制到outfile_path中并重新设置needcpy_outfile为0 。如果当前分割结果为"<“,说明接下来将要读入输入重定向的文件,设置needcpy_infile和Is_in_redirect为1;如果当前分割结果为”>“,说明接下来将要读入输出重定向(覆盖写)的文件,设置needcpy_outfile和Is_out_redirect_cov为1 。如果当前分割结果为”>>",说明接下来将要读入输出重定向(追加写)的文件,设置needcpy_outfile和Is_out_redirect_app为1
int Split_command()
{
for(int i=0; i<MAX_COMMAND_NUM; i++)
commands[i] = NULL;//初始化commands的每一项为NULL
command_num = 0;
int needcpy_infile = 0;
int needcpy_outfile = 0;
char* temp;//指向第一个分割的值
char* delim = " \n\t";
temp = strtok(inbuf,delim);
if(!temp)
return 1;
commands[0] = temp;
command_num++;
while(command_num < MAX_COMMAND_NUM && (commands[command_num] = strtok(NULL,delim)) ){
if(needcpy_infile){
strcpy(infile_path,commands[command_num]);
needcpy_infile = 0;
continue;
}
if(needcpy_outfile){
strcpy(outfile_path,commands[command_num]);
needcpy_outfile = 0;
continue;
}
if(strcmp(commands[command_num],"<") == 0){
needcpy_infile = 1;
Is_in_redirect = 1;
continue;
}
if(strcmp(commands[command_num],">") == 0){
needcpy_outfile = 1;
Is_out_redirect_cov = 1;
continue;
}
if(strcmp(commands[command_num],">>") == 0){
needcpy_outfile = 1;
Is_out_redirect_app = 1;
continue;
}
command_num++; //计数
}
if(command_num >= MAX_COMMAND_NUM) //参数过多,返回2
return 2;
return 0;// 正常结束返回0
}
实现函数:My_command()
函数功能:选择并执行myshell的内建命令
实现原理:根据不同的命令选择相应的函数执行内建命令
int My_command(int count, int index)
{
if(strcmp(commands[index],"clr") == 0)
Clr();
else if(strcmp(commands[index],"copy") == 0)
Copy();
else if(strcmp(commands[index],"environ") == 0)
Environ();
else if(strcmp(commands[index],"pwd") == 0)
Pwd();
else if(strcmp(commands[index],"exit") == 0)
Exit();
else if(strcmp(commands[index],"time") == 0)
Time();
else if(strcmp(commands[index],"touch") == 0)
Touch();
else if(strcmp(commands[index],"cd") == 0)
Cd(count,index);
else if(strcmp(commands[index],"echo") == 0)
Echo(count,index);
else if(strcmp(commands[index],"exec") == 0)
Exec(count,index);
else if(strcmp(commands[index],"set") == 0)
Set(count,index);
else if(strcmp(commands[index],"unset") == 0)
Unset(count,index);
else if(strcmp(commands[index],"help") == 0)
Help();
else
return 1;
return 0;
}
实现函数:Run_command()
函数功能:执行所有命令的接口,包括内建命令和外部命令。
实现原理:首先判断命令是否包含输入或者输出重定向,以及判断是内部命令还是外部命令。然后根据命令所属的种类执行不同的操作。
void Run_command()
{
//分别备份stdin和stdout的文件描述符到stdinFd和stdoutFd中
int stdinFd = dup(fileno(stdin));
int stdoutFd = dup(fileno(stdout));
//命令中包含了输入重定向
if(Is_in_redirect){
int fileFd = 0;
//以只读方式打开infile_path
fileFd = open(infile_path, O_RDONLY, 0666);
//将标准输入重定向到infile_path,如果失败则输出错误信息
if(dup2(fileFd, fileno(stdin)) == -1)
fprintf(stderr,RED "[zyw_shell] Error: dup2() 失败!\n");
//关闭文件
close(fileFd);
}
//命令中包含了输出重定向(覆盖写)
if(Is_out_redirect_cov){
int fileFd = 0;
//以读写,覆盖写方式打开outfile_path
fileFd = open(outfile_path, O_RDWR | O_CREAT | O_TRUNC, 0666);
//将标准输出重定向到outfile_path,如果失败则输出错误信息
if(dup2(fileFd, fileno(stdout)) == -1)
fprintf(stderr,RED "[zyw_shell] Error: dup2() 失败!\n");
//关闭文件
close(fileFd);
}
//命令中包含了输出重定向(追加写)
if(Is_out_redirect_app){
int fileFd = 0;
//以读写,追加写方式打开outfile_path
fileFd = open(outfile_path, O_RDWR | O_CREAT | O_APPEND, 0666);
//将标准输出重定向到outfile_path,如果失败则输出错误信息
if(dup2(fileFd, fileno(stdout)) == -1)
fprintf(stderr,RED "[zyw_shell] Error: dup2() is 失败!\n");
//关闭文件
close(fileFd);
}
//调用My_command()函数执行命令,若该函数返回值为1,说明为外部命令,需要继续处理
if(My_command(command_num,0) == 1){
//调用execvp()执行外部命令,如果失败则输出错误信息
if(execvp(commands[0],commands)!=0)
fprintf(stderr, RED "[zyw_shell] Error: 无法找到命令 \"%s\"!\n", commands[0]);
//正常退出子进程
exit(EXIT_SUCCESS);
}
}
//如果Is_in_redirect为1,说明之前进行了输入重定向,需要复原
if(Is_in_redirect){
//复原标准输入,失败则输出错误信息
if(dup2(stdinFd, fileno(stdin)) == -1)
fprintf(stderr,RED "[zyw_shell] Error: dup2() 失败!\n");
//关闭文件
close(stdinFd);
}
//如果Is_out_redirect_cov或Is_out_redirect_app为1,说明之前进行了输出重定向,需要复原
if(Is_out_redirect_cov || Is_out_redirect_app){
//复原标准输出,失败则输出错误信息
if(dup2(stdoutFd, fileno(stdout)) == -1)
fprintf(stderr,RED "[zyw_shell] Error: dup2() 失败\n");
//关闭文件
close(stdoutFd);
}
}
int main(int argc, char **argv)
{
int script_flag = 0; //script_flag用来指示脚本文件是否已经被打开
FILE* fp;
setenv("SHELL","/home/zyw/myshell_zyw",1);
while(1){
Init();// 初始化
//没有参数传入,则调用Display_prompt()函数显示命令提示符
if(argc == 1)
DisplayInfo();
//没有参数传入时将NULL作为Get_command()函数的参数,否则将fp作为Get_command()函数的参数
if(argc == 1)
Get_command(NULL);
else{
//script_flag为0,即脚本文件还未打开
if(!script_flag){
//设置script_flag为1,并打开脚本文件将指针保存到fp中
script_flag = 1;
//如果fp为NULL,说明打开失败,则输出错误信息并退出
if(!(fp = fopen(argv[1], "r"))){
fprintf(stderr, RED "[myshell] Error: 无法打开脚本文件 \"%s\"!\n", argv[1]);
exit(1);
}
}
//script_flag为1,即脚本文件已经打开,调用Get_command(fp)函数读取命令
else
Get_command(fp);
}
//调用Split_command()函数分割读取的输入并将函数返回值存到flag中
int flag = Split_command();
//如果flag的值为1,说明用户没有输入内容或者输入均为分隔符,直接continue
if(flag == 1)
continue;
//如果flag的值为2,说明用户输入的参数个数过多,输出错误信息并continue
else if(flag == 2){
fprintf(stderr, RED "[myshell] Error: 参数过多!\n");
continue;
}
//命令正常,则调用Run_command()函数解析命令并相应执行
else
Run_command();
}
return 0;
}
void Get_command(FILE* script_fp)
{
if(!script_fp)
fgets(inbuf,MAX_INBUF_SIZE-1,stdin);
else{
if(fgets(inbuf,MAX_INBUF_SIZE-1,script_fp) == NULL){
fclose(script_fp);
exit(0);
}
}
}
进入shell
首先输入命令make
进行编译链接,然后输入./myshell
切换到自制的shell。
help命令
environ
可以和ubuntu 自带的bash shell 对比,结果除了第一行的SHELL
不一致外,其余的都相同。
cd
测试用例1:输入cd+指定目录
时,可以正常的切换路径
测试用例2:输入cd ../
成功的退回上一级目录
测试用例3:输入cd xx
, 提示错误找不到该路径
测试用例4:输入cd x y
, 提示输入的参数过多
测试用例2:copy c.txt b.txt
报错无法打开文件c.txt
测试用例3:copy a.txt
报错输入参数只能有两个
pwd
输入pwd
后可以正常的打印当前的目录
exit
输入命令exit
后退出自制的shell
time
输入time
可以正常的显示年月日,星期和时间。与电脑的系统时间比对一致。
touch
测试用例1:touch a.txt
,成功创建文件a.txt
测试用例2:touch b.txt s
报错输入的参数只能有一个!
测试用例3:touch
报错输入的参数只能有一个!
echo
测试用例1:输入echo Hello, my name is zyw
后,可以成功的显示Hello, my name is zyw
exec
输入exec ls
, 可以看到执行完ls命令后成功的退出自制的shell。
set
测试用例1:输入set
后可以显示所有的环境变量
测试用例2: 输入 set SHELL /home/shell
成功的将SHELL
的环境变量设为了/home/shell
测试用例3:输入set xx
,报错参数的个数只能是0个或者两个
测试用例4:输入set xyz /home
,报错没有该环境变量
unset
输入unset SHELL
后,再次输入set
命令查看环境变量,发现SHELL
被成功的置为空。
外部命令
测试用例1:输入外部命令ls -al
测试用例2:输入外部命令ps
测试用例3:输入外部命令users
测试用例4:输入外部命令who
执行脚本
首先输入cat testshell.sh
查看该脚本文件
然后输入./myshell testshell.sh
成功的执行了该脚本文件。
I/O重定向
测试用例1:输出重定向>
,当没有输出文件时,会创建一个文件,并将输出的内容写入到该文件中。
测试用例2:输出重定向>
,当有该文件时,会将输出的内容覆盖写入到该文件中
测试用例3:输出重定向>>
将输出的内容追加写入到该文件中。
测试用例4:输入重定向<
将in.txt的内容输出到list.txt,并覆盖其原有的内容。