@Lxjeng
2016-04-26T12:14:44.000000Z
字数 2649
阅读 921
- 姓名:梁晓静 学号:1405010530
- 实现一个简单的shell(命令行解释器)
- 在Linux系统下,通过c语言编程完成一个自己的shell
- Linux系统虚拟机
你的 shell 类似于 sh,bash,csh 等,必须支持以下内部命令:
- cd <目录>:更改当前的工作目录到另一个<目录>。如果<目录>未指定,输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示。这个命令应该也能改变 PWD 的环境变量。
- environ:出所有环境变量字符串的设置(类似于 Unix系统下的env命令)。
- echo <内容 >:显示 echo 后的内容且换行。
- help:简短概要的输出你的 shell 的使用方法和基本功能。
- jobs:输出 shell当前的一系列子进程,必须提供子进程的命名和PID号。
- quit,exit,bye:退出 shell。


#include <stdio.h>#include <string.h>#include <unistd.h>#include <sys/wait.h>#include <stdlib.h>#include <sys/types.h>#include <errno.h>#include <sys/stat.h>void help(){printf( " 帮助\n"" 命令 功能\n"" cd 更改当前的工作目录到另一个<目录>\n"" environ 列出所有环境变量字符串的设置\n"" echo 显示echo后面的内容并换行\n"" help 显示帮助信息\n"" jobs 输出shell当前的一系列子进程,包括子进程的命名和PID号\n"" ls 显示当前目录的所有文件\n"" quit|exit|bye 退出shell\n");}int main(){char n[100];pid_t pid;char buf1[1024],buf2[1024];int i=0;char *p;int argc=0;char *argv[10];while(1){argc=0; //参数个数初始化为0for(i=0; i<10; i++){argv[i]=NULL;//字符串数组赋初值}memset(n,0,100);//数组清零(将n数组的前100个字节用0替换)char *username=getenv("USER");gethostname(buf1,sizeof buf1);//返回本地主机的标准主机名getwd(buf2); //获取当前路径strtok(buf1,"");printf("%s@%s %s $ ",username,buf1,buf2);fgets(n,100,stdin);n[strlen(n)-1]=0;p=strtok(n," "); //根据空格分开,并获得头指令while(p&&argc<10){argv[argc]=p;p=strtok(NULL," ");argc++;}if(argc<10){argv[argc]=NULL;} //字符截取if(!strcmp(argv[0],"exit")||!strcmp(argv[0],"quit")||!strcmp(argv[0],"bye"))break;else if(!strcmp(argv[0],"cd")){if(chdir(argv[1])<0)printf(" 打开失败!\n");}else if(!strcmp(argv[0],"ls")){if((pid=fork())<0){printf(" fork error\n");exit(0);}else if(pid==0){if(execl("/bin/ls","ls",NULL)<0);printf(" execl error\n");exit(0);}waitpid(pid,0,0);}else if(!strcmp(argv[0],"echo")){printf("%s\n",argv[1]);}else if(!strcmp(argv[0],"help"))help();else if(!strcmp(argv[0],"env")){if((pid=fork())<0){printf(" fork error\n");exit(0);}else if(pid==0){if(execl("/bin/env","env",NULL)<0)printf(" execl error\n");exit(0);}waitpid(pid,0,0);}else if(!strcmp(argv[0],"jobs"))system("ps"); //调用ps函数查看子进程}return 0;}
- 1、本实验的关键在于shell源码的编写,一开始无从下手,直接在百度上搜的又不全面,但是通过对其中一段代码的学习,大致思路便清晰了,也理解了如fork,memset,strtok,chdir等函数的功能和用法。
- 2、关键思路在于输入命令行与相应字符串对比,如果相等就执行。此时用到strcmp函数,如果两个字符串相等就返回0,所以就决定了if语句中的判断条件。实验用到argv[]字符串数组,argv[0]存储命令行字符,argv【1】存储相应目录名和内容。通过strtok函数进行字符串截取,cd中,当argv【1】<0表示输入为空,此时显示“打开失败!”。echo中,直接通过printf函数输出argv【1】表示的内容。
- 3、在 Linux 中创建子进程要使用fork()函数,执行新的命令要使用exec()系列函数,这里使用execl(const char *path, const char *arg,...)函数,const char *arg以及省略号代表的参数可被视为arg0,arg1,...,argn.他们合起来描述了指向NULL结尾的字符串的指针列表,即执行程序的参数列表.作为约定,第一个 arg 参数应该指向执行程序名自身,参数列表必须用 NULL 指针结束. shell等待子进程结束才能执行新的命令,所以使用waitpid()函数,结束终止进程使用 exit()函数。
- 4、好吧,代码是借鉴班上大神的,不过我改成了字符串截取然后进行比较,而不是直接通过输入字符串数组n进行操作。花了很久差不多把每行代码都弄懂,一开始最大的误解,我以为每个命令行都要自己去编写代码实现功能,其实是可以调用系统自身就有的!