myshell——拥有简单功能的Linux外壳

功能概述

  1. 支持当前环境变量中的命令,例如cdlsmvrm

  2. 支持简单的输入重定向,输出重定向,输出追加重定向

  3. 支持后台运行

  4. 支持管道连接两条基本命令

    (但每次执行只能出现一次以上操作,不支持组合使用)

功能的有关系统调用

总体流程为

打印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下是否有对应的可执行文件。使用DIRdirent结构体,引用头文件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()函数关闭目录句柄。

posted @ 2021-08-15 17:06  Fight扬尘  阅读(499)  评论(0)    收藏  举报