myshell——拥有简单功能的Linux外壳
功能概述
-
支持当前环境变量中的命令,例如
cd、ls、mv、rm等 -
支持简单的输入重定向,输出重定向,输出追加重定向
-
支持后台运行
-
支持管道连接两条基本命令
(但每次执行只能出现一次以上操作,不支持组合使用)
功能的有关系统调用
总体流程为
打印shell提示符
输入指令
cmd判断是否退出
解析
cmd及参数判断
cmd的类型执行
打印shell提示符时,通过系统调用char *getcwd(char *buff, size_t size)获取当前目录
char *msg;
msg = (char *)malloc(256);
getcwd(msg, 256);//获取当前目录
char delim[] = "/";
char* catalog;
pwdOut(msg, delim, &catalog);
printf("myshell: ycg@cg-virtual-machine:%s$ ", catalog);
执行cd命令时,通过系统调用int chdir(const char *pathname)改变当前目录
if(arg[0] != NULL && strcmp(arg[0], "cd") == 0) {
if (arg[1] == NULL) {
return ;
} else if(strcmp(arg[1], "~") == 0) {
strcpy(arg[1], "/home/ycg/");
}
//改变当前工作目录
int ret = chdir(arg[1]);
if(ret == -1) {
perror("cd");
}
return;
}
调用pid_t fork(void)创建子进程,统一让子进程(pid == 0)执行基本命令、重定向或者管道左边的命令。
执行命令
通过调用int execvp(cons char *filename, char *const argv[]),在PATH环境变量下寻找文件名为filename的可执行文件,并将参数argv[]传入并执行,其中argv[]需要以空指针NULL结尾
重定向
通过调用int open(const char *pathname, int oflag, [mode_t mode]),打开文件pathname,获得该文件的文件描述符,常用oflag有
│O_RDONLY│读文件 │ //用于输入重定向
│O_WRONLY│写文件 │
│O_RDWR │即读也写 │ //用于输出重定向
│O_APPEND│即读也写,但每次写总是在文件尾添加 │ //用于追加重定向
│O_CREAT │若文件存在,此标志无用;若不存在,建新文件 │
│O_TRUNC │若文件存在,则长度被截为0,属性不变 │
后通过调用int dup2(int filedes1, int filedes2),将filedes2指向的文件重定向至filedes1所指向的文件,其中filedes2的选项可为
- 0:标准输入
stdin - 1:标准输出
stdout - 2:标准异常
stderr
随用调用execvp函数
以输入重定向为例:
if(pid == 0){
if(!find_cmd(arg[0])){
printf("%s: command is not found\n",arg[0]);
exit(0);
}
execvp(arg[0], arg);
exit(0);
}
管道
利用fork函数创建子进程,子进程执行管道左边命令(相当于一次输出重定向),父进程调用pid_t waitpid(pid_t pid, int *statloc, int options)等待子进程结束,随后父进程执行管道右边命令(相当于输入重定向)。
if(pid2 == 0) {
//子进程将左边的命令的stdout写入fd2所指向的文件表项
fd2 = open("./tmp", O_WRONLY|O_CREAT|O_TRUNC, 0644);
dup2(fd2,1); /*子进程执行写入的好处是父进程的stdin不受影响 */
execvp(arg[0],arg);
exit(0);
}
/*父进程挂起,子进程写入左边的命令后父进程继续执行 */
if(waitpid(pid2, &status2, 0) == -1) {
printf("wait for child process error\n");
}
//查看文件路径是否存在
if(!find_cmd(argnext[0])) {
printf("%s: command is not found\n",argnext[0]);
exit(0);
}
/*父进程执行管道右侧的shell命令,同时把其stdin改为fd2所指向的文件表项 */
fd2 = open("./tmp/", O_RDONLY);
dup2(fd2,0);
execvp(argnext[0],argnext);
/*删除暂存的文件 */
if(remove("./tmp/") < 0) { //系统调用int remove(const char *pathname)
printf("remove error\n");
}
exit(0);
最后,如果有后台运行符,则父进程无需等待直接结束,否则调用waitpid等待子进程结束
命令查找
对输入的指令判断当前PATH下是否有对应的可执行文件。使用DIR和dirent结构体,引用头文件dirent.h。
通过opendir()函数获取该路径下目录句柄的DIR结构体指针。函数原型为:
DIR* opendir (const char * path ); //(获取path子目录下的所由文件和目录的列表,如果path是个文件则返回值为NULL)
随后通过readdir()函数获取该DIR所指向的目录下每个文件对应的dirent结构体指针。函数原型为:
struct dirent* readdir(DIR* dir_handle); //(个人理解循环读取dir_handle,目录和文件都读)
其中dirent中成员属性d_name为文件名,即命令。遍历寻找是否有对应的命令。
最后通过closedir()函数关闭目录句柄。

浙公网安备 33010602011771号