实验四 Shell 解释程序的分析与设计
白凤伟 10120451
实验目的
Shell 是操作系统最外面的一层,处理用户与操作系统之间的交互动作,并且给出相应的操作系统
的输出结果。Shell 基本上是一个命令解释器,类似于 DOS 下的 command.com。它接收用户命令,然后
调用相应的功能处理程序。通过本次实验,我们可以简单了解操作系统和用户间的交互。
实验内容
编写 C 代码完成 Shell 解释命令的步骤:
(1)打印提示符
(2)得到命令行
(3)解析命令
(4)查找文件
(5)准备参数
(6)执行命令
实验过程
Shell 解释命令的流程如下:
(1)打印提示符
在本实验中,将当前工作目录的绝对路径名和符号“->$”一并作为命令提示符。
(2)得到命令行
为了得到命令行,shell 执行一个阻塞读操作,让执行 shell 的进程进入睡眠状态,直到用户键入一
个命令行作为对提示符的响应。
(3)解析命令
解析程序从命令行的左边开始扫描命令行,直到遇到第一个空白字符。第一个单词是命令的名称,
而后面的则是命令参数。
(4)查找文件
Shell 为每个用户提供了一组环境变量,这些变量定义在用户的.login 文件中。其中 PATH 环境变量
是一个有序的绝对路径列表,它指明了 shell 应该在什么地方寻找命令文件。如果在任何指定的目录中
都没有找到与命令同名的文件,那么 shell 将提示用户无法找到命令。
(5)准备参数
在本实验中,将输入的命令行解析到数组 Real_Command 中,Real_Command[0]代表命令名字,其
它位置存放的是参数。
(6)执行命令
Shell 需要执行指定文件中的可执行程序,并保护原始进程不被破坏。Shell 通过采用系统调用 fork()、
execv()和 wait()使用多进程来达到这个目的。
本实验的程序实现见附录。
实验结果分析
将 C 程序文件编译取名为 sh,运行 sh 后如图 1 所示,即出现提示符:/home/vincent->$,前半部分
即为当前工作目录的绝对路径。
接下来我们输入命令 ls,命令执行结果如图 2 所示,能够正确显示此目录下的文件。
图 1
再输入 file sh easyshell.c,结果如图 3 所示,进一步说明命令的执行是正确的。
图 2
当我们随便输入一列字母时,它并不是命令语句,因此会提示错误,并没有此命令,结果如图 4
所示。
图 3
最后,我们输入命令 quit,便会结束此程序,因为程序中设置的是输入 quit 就退出,结果如图 5 所
图 4
示。
此程序的运行比较成功,从结果中还可以看到多进程的执行过程。通过编写 shell 解释命令的步骤
的程序,我们知道了操作系统是如何和用户进行交互的,进一步理解了 shell 作为解释程序的含义。
图 5
附录
文件名:easyshell.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LENGTH 80
char *Input_Command(){//输入命令
char *Input_Array;
if ((Input_Array = (char *)malloc(MAX_LENGTH * (sizeof (char)))) == 0 ) {
printf("error! can't malloc enough space for buffer\n");
return NULL;
}
fgets(Input_Array,MAX_LENGTH,stdin);
Input_Array[strlen(Input_Array)-1] = '\0';
return Input_Array;
}
char *Command_File_Exist(char *Command){//寻找输入命令所在文件的绝对路径
char *File_Path; //存储输入的命令所在文件的绝对路径
char *path;//存储环境变量中的 PATH 值
if ((path = getenv("PATH"))!=0){
printf("PATH 为: %s\n",path);
int File_Path_Length = strlen(path) + strlen(Command) + 1;
File_Path = (char *)malloc(File_Path_Length * (sizeof(char)));
int i = 0;
int j = 0;
for (i = 0;i < strlen(path);i++){
if(path[i]==':' || path[i]=='\0'){//找到 PATH 中的一个绝对路径名
File_Path[j++] = '/';
File_Path[j] = '\0';
strcat(File_Path,Command);//将命令名加到绝对路径名后,用于查找
if (access(File_Path,F_OK) == 0) {//找到文件
return File_Path;
}
else
j = 0;
}
else{File_Path[j++] = path[i];}
}
return NULL;
return NULL;
}
else
}
char **Analyse_Command(char *Input_Array){//分析输入命令
char **Real_Command;//存储分析结果
char *args;
if ((Real_Command = (char **)malloc(MAX_LENGTH * (sizeof(char *)))) == 0 ){
printf("error! can't malloc enough space!\n");
return 0;
}
if ((args = (char *)malloc(MAX_LENGTH * (sizeof(char)))) == 0 ){
printf("error! can't malloc enough space!\n");
return 0;
}
int i,j = 0,k = 0;
for (i = 0; i < strlen(Input_Array);i++){
if (Input_Array[i] != ' ')//输入命令以空格为间隔,第一个空格前为命令,其它为参数
args[j++] = Input_Array[i];
else {
args[j] = '\0';
if (strlen(args) > 0){
if ((Real_Command[k] = (char *)malloc(strlen(args) * (sizeof (char)))) == 0){
printf("error! can't malloc enough space!\n");
return 0;
}
strcpy(Real_Command[k++],args);
j = 0;
}
}
}
args[j] = '\0';
if (strlen(args) > 0){//存储最后一个参数
if ((Real_Command[k] = (char *)malloc(strlen(args) * (sizeof (char)))) == 0){
printf("error! can't malloc enough space!\n");
return 0;
}
strcpy(Real_Command[k++],args);
}
Real_Command[k] = NULL;
return Real_Command;
}
int main(){
char *Input_Array;//输入命令的数组
char *Current_Path;//当前工作目录的绝对路径
char **Real_Command;//存储分析输入命令的结果
if ((Input_Array = (char *)malloc(MAX_LENGTH * (sizeof (char)))) == 0 ) {
printf("error! can't malloc enough space for buffer\n");
return (0);
}
while (1){
Current_Path = getcwd(NULL,0);
printf ("%s->$",Current_Path);//打印提示符,形式为:当前路径->$
Input_Array = Input_Command();
if (strlen(Input_Array) != 0){
if (strcmp(Input_Array,"quit") == 0){//输入为 quit 时退出程序
printf("Good Bye!\n");
break;
}
else if(strcmp(Input_Array,"cd")==0){//输入为 cd 时转化路径
if (Real_Command[1] != NULL){
if (chdir(Real_Command[1]) < 0)//转换失败
fprintf(stderr,"cd ERROR!\n");
}
else
}
else{
fprintf(stderr,"no input\n");
Real_Command=Analyse_Command(Input_Array);
char *Full_PathName;
Full_PathName = Command_File_Exist(Real_Command[0]);
if (Full_PathName != NULL){
int pid=0;
pid=fork();
if(pid>0){//父进程
wait(0);
printf("This is the parent's pid:%d\n",pid);
}
else{//子进程
printf("This is the child's pid:%d\n",pid);
execv(Full_PathName,Real_Command);
exit(0);
}
}
else{fprintf(stderr,"error:the command isn't exsit!\n");}
}
}
}
return 1;
}