Tiny-shell:一个模仿bash的极简shell (一)
概述
通过构建一个简单的shell,能够对shell的工作原理进行一些了解。主要有:
- 重定向
- 流水线
- 前台信号处理
- 进程组
- 后台进程
- 作业控制
这篇文章里先实现一个极简的shell,后续再不断对功能进行完善添加。
思路
在main
函数里,我们需要一个循环来一直进行输出,打印shell的提示符,用于提示命令的输入。同时,我们需要对从标准输入的命令进行处理,也就是字符串的处理。当按下回车时,需要我们fork
出一个子进程,并调用execvp
来执行处理过的命令。然后main
函数wait
子进程执行完毕继续打印新的一行提示符即可。这就是一个极简的shell。
函数说明
make_argv
首先我们需要写一个处理字符串的函数int make_argv(const char *s, const char *delimiters, char ***argvp)
。s
即是我们传入的字符串,比如ls -l
,我们要做的就是将此字符串处理成一个参数数组,便于传递给int execvp(const char *file, char *const argv[])
。函数中主要涉及用strtok
来进行处理。
execute_cmd
这个函数用于调用make_argv
,并进行检错,然后调用execvp
。
main
main
函数里进行fork
,并调用execute_cmd
。
运行情况
编译:`gcc -o tsh1 tsh1.c execute_cmd_simple.c make_argv.c"
tsh1>> tsh1>> ls execute_cmd_simple.c make_argv.c tsh1 tsh1.c tsh1>> tsh1>> echo "This is a tiny shell~" "This is a tiny shell~" tsh1>> q
源代码
tsh1.c
:
/** * @Filename: tsh1.c * @Description: tiny shell 的main函数 */ #include <limits.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #define PROMPT_STRING "tsh1>> " #define QUIT_STRING "q" /* 按q退出 */ void execute_cmd(char *incmd); int main (void) { pid_t childpid; char inbuf[MAX_CANON]; int len; for( ; ; ) { if (fputs(PROMPT_STRING, stdout) == EOF) continue; if (fgets(inbuf, MAX_CANON, stdin) == NULL) continue; len = strlen(inbuf); if (len == 1 && inbuf[len - 1] == '\n') /* 若直接按回车就忽略 */ continue; if (inbuf[len - 1] == '\n') inbuf[len - 1] = 0; if (strcmp(inbuf, QUIT_STRING) == 0) break; if ((childpid = fork()) == -1) perror("Failed to fork child"); else if (childpid == 0) { /* 子进程 */ execute_cmd(inbuf); return 1; } else wait(NULL); } return 0; }
execite_cmd_simple.c
:
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define BLANK_STRING " " int make_argv(const char *s, const char *delimiters, char ***argvp); void execute_cmd(char *incmd) { char **chargv; if (make_argv(incmd, BLANK_STRING, &chargv) <= 0) { fprintf(stderr, "Failed to parse command line\n"); exit(1); } execvp(chargv[0], chargv); perror("Failed to execute command"); exit(1); }
make_argv.c
:
/** * @Filename: make_argv.c * @Description: 处理字符串,形成参数数组 */ #include <errno.h> #include <stdlib.h> #include <string.h> #include <stdio.h> /** s是传入字符串 * delimiters是分隔符 * argvp是形成的参数数组 */ int make_argv(const char *s, const char *delimiters, char ***argvp) { int error; int i; int numtokens; /* 形成的token总数 */ const char *snew; char *t; if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) { errno = EINVAL; return -1; } *argvp = NULL; snew = s + strspn(s, delimiters); /* snew是字符串的真正的开始,去掉前面多余的分隔符*/ if ((t = malloc(strlen(snew) + 1)) == NULL) return -1; strcpy(t, snew); numtokens = 0; if (strtok(t, delimiters) != NULL) /* 计算token的数目,for从1开始是因为参数数组最后一个空间要给NULL */ for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; /* 创建参数数组 */ if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) { error = errno; free(t); errno = error; return -1; } /* 对每个子串进行拷贝 */ if (numtokens == 0) free(t); else { strcpy(t, snew); **argvp = strtok(t, delimiters); for (i = 1; i < numtokens; i++) (*argvp)[i] = strtok(NULL, delimiters); } (*argvp)[numtokens] = NULL; /* 最后放NULL */ return numtokens; }
来源:https://www.cnblogs.com/chrisynl/p/12374131.html