代码逻辑框架
(边写边查)
- 命令行提示符,fflush 刷新显示
- 获取 输入的 有效字符串,定义一个字符数组,用 fgets 从键盘上获取(注意处理命令行输入时的回车)
- 字符串切割,C语言函数 strtok,子串保存到指针数组
- 创建子进程,执行代码
增加:
-
ls 的配色方案
-
内建命令的处理
-
cd ..
cd /
这样的命令,需要让 bash 自己执行的,叫做 内建命令 / 内置命令 -
export
,一般用户自定义的环境变量,在 bash 中要用户自己来进行维护,不要用一个经常被覆盖的缓冲区来保存环境变量!!! -
env
,我们一般需要 env 时,都要查看的是自己进程的环境列表,这里就是查看 bash 的环境列表。 -
echo
,利用指针查找和输出 -
echo $?
,取出子进程退出码
-
>
重定向符,实现方法见注释
其实我们之前提到过的几乎所有环境变量命令,都是 内建命令。
makefile
mybash:mybash.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f mybash
mybash.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX 1024
#define ARGC 64
#define SEP " "
enum redir{
REDIR_INPUT = 0,
REDIR_OUTPUT,
REDIR_APPEND,
REDIR_NONE
};
char * checkDir(char commandstr[], redir &redir_type)
{
// 1. 检测 commandstr 内部是否有 > < >>
// 2. 如果有,根据 > < >>,设置 redir_type = X
// 3. 将 > >> < -> '\0',即将 commandstr 设置成两部分
// 4. 保存文件名,并返回
// 5. 如果没有重定向,直接放回
return NULL;
}
int split(char *commandstr, char *argv[])
{
assert(commandstr);
assert(argv);
argv[0] = strtok(commandstr, SEP);
if(argv[0] == NULL) return -1;
int i = 1;
while((argv[i++] = strtok(NULL, SEP)));
//int i = 1;
//while(1)
//{
// argv[i] = strtok(NULL, SEP);
// if(argv[i] == NULL) break;
// i++;
//}
return 0;
}
void debugPrint(char *argv[])
{
for(int i = 0; argv[i]; i++)
{
printf("%d: %s\n", i, argv[i]);
}
}
void showEnv()
{
extern char **environ;
for(int i = 0; environ[i]; i++) printf("%d:%s\n", i, environ[i]);
}
int main()
{
//当我们在进行 env 查看的时候,我们想查的是谁的环境变量列表?父进程 bash 的环境变量列表
int last_exit = 0;
char myenv[32][256];
int env_index = 0;
while(1)
{
char commandstr[MAX] = {0}; // 命令行输入的,完整字符串
char *argv[ARGC] = {NULL}; // 命令行输入的,子串
redir redir_type = REDIR_NONE;
// 1
printf("[Stella@hostname currpath]# ");
fflush(stdout); // 把数据从缓冲区刷新显示出来
// cat Makefile > log.txt
// cat Makefile >> log.txt
// cat Makefile < log.txt
// 2
char *s = fgets(commandstr, sizeof(commandstr), stdin);
assert(s);
(void)s; // 保证在release方式发布的时候,因为去掉assert了,所以s就没有被使用,而带来的编译告警, 什么都没做,但是充当一次使用
// abcd\n\0
commandstr[strlen(commandstr)-1] = '\0';
// (3)【重定向】
char *filename = checkDir(commandstr, &redir_type);
// 3
// "ls -a -l" -> "ls" "-a" "-l"
int n = split(commandstr, argv);
if(n != 0) continue;
//debugPrint(argv);
// (1)【添加 ls 命令的配色方案】
if(strcmp(argv[0], "ls") == 0)
{
int pos = 0;
while(argv[pos]) pos++;
argv[pos++] = (char*)"--color=auto";加配色方案
argv[pos] = NULL; // 比较安全的做法
}
// (2)【内建命令】
// cd .. / cd /: 让bash自己执行的命令,我们称之为 内建命令/内置命令
if(strcmp(argv[0], "cd") == 0)
{
//说到底,cd 命令,重要的表现就如同 bash 自己调用了对应的函数
if(argv[1] != NULL) chdir(argv[1]); // chdir是一个系统调用函数,可以改变当前工作路径
continue;
}
else if(strcmp(argv[0], "export") == 0) // 其实我们之前学习到的所有的(几乎)环境变量命令,都是内建命令
{
if(argv[1] != NULL){
strcpy(myenv[env_index], argv[1]);
putenv(myenv[env_index++]);
}
continue;
}
else if(strcmp(argv[0], "env") == 0)
{
showEnv();
continue;
}
else if(strcmp(argv[0], "echo") == 0)
{
// echo $PATH
const char *target_env = NULL;
if(argv[1][0] == '$')
{
if(argv[1][1] == '?'){
printf("%d\n", last_exit);
continue;
}
else target_env = getenv(argv[1]+1); // "abcdefg
if(target_env != NULL) printf("%s=%s\n", argv[1]+1, target_env);
}
continue;
}
// 4
// version 1
pid_t id = fork();
assert(id >= 0);
(void)id;
if(id == 0)
{
if(redir_type != REDIR_NONE)
{
// 1. 存在文件的
// 2. redir_type获取到
// 3. dup2;
}
//child
execvp(argv[0], argv);
exit(1);
}
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if(ret > 0){
last_exit = WEXITSTATUS(status);
}
//printf("%s\n", commandstr);
}
}
版权声明:本文为m0_67470729原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。