(一)bash流程框架图:

在这里插入图片描述

(二)代码实现

  • mybash.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef MYBASH_H
#define MYBASH_H
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <pwd.h>
#include <sys/utsname.h>

typedef enum returncode
{
//地址空
ADDR_NULL = 0,
//退出 1
EXIT,
//cd 2
MYCD,
//exec 3
EXEC
}returncode;

//输出提示符信息
void OutPutInfo();

//从键盘获取命令
char* GetCmd();

//命令解析
returncode CmdResolve(char* cmd, char* cmdbuff[]);

//进程替换
void ChildExec(char* cmdbuff[], const char* cmd);

//处理僵尸进程
void Zombie();

//判断是否在后台运行
int IsBack(const char* cmdbuff);

//切换目录
void Mycd(const char* path);

#endif
  • mybash.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "mybash.h"

//输出提示符信息
void OutPutInfo()
{
// struct passwd
// {
// char *pw_name; /* username */
// char *pw_passwd; /* user password */
// uid_t pw_uid; /* user ID */
// gid_t pw_gid; /* group ID */
// char *pw_gecos; /* user information */
// char *pw_dir; /* home directory */
// char *pw_shell; /* shell program */
// };
char flg = '$';
struct passwd* pw = getpwuid(getuid());
if(pw == NULL)
{
return;
}
//判断是普通用户还是root用户
if(pw->pw_uid == 0)
{
flg = '#';
}
/*
*普通用户:
* 用户名@主机名:当前工作路径名$
*root用户
* root@主机名:当前工作路径名#
*/
//获取主机名
struct utsname buff;
uname(&buff);
//获取当前工作路径的绝对路径
char pathname[128] = {0};
getcwd(pathname, 127);

//家目录的长度
int len_homepath = strlen(pw->pw_dir);
char* p = pathname + len_homepath;
//如果当前的工作路径中包含家目录,就使用"~"替换前面的家目录部分
if(strncmp(pathname, pw->pw_dir, len_homepath) == 0)
{
memset(pathname, 0, len_homepath);
strcat(pathname, "~");
strcat(pathname, p);
}
printf("%s@%s:%s%c ",pw->pw_name,buff.nodename, pathname,flg);
fflush(stdout);
}

//从键盘获取命令
char* GetCmd()
{
char* cmd = (char*)malloc(sizeof(char) * 128);
memset(cmd, 0, sizeof(char) * 128);
fgets(cmd, 127, stdin);
cmd[strlen(cmd) - 1] = '\0'; //去掉'\n'

return cmd;
}

//命令解析
returncode CmdResolve(char* cmd, char* cmdbuff[])
{
if(cmd == NULL || cmdbuff == NULL)
{
return ADDR_NULL;
}
//分割指令
char *s = strtok(cmd, " ");
int i = 0;
while (s != NULL)
{
cmdbuff[i++] = s;
s = strtok(NULL, " ");
}
//最后一个置空
cmdbuff[i] = NULL;

if(strcmp(cmdbuff[0], "exit") == 0)
{
//退出
return EXIT;
}

if(strcmp(cmdbuff[0], "cd") == 0)
{
//切换目录
return MYCD;
}
else
{
//进程替换
return EXEC;
}
}

//进程替换
void ChildExec(char* cmdbuff[], const char* cmd)
{
if(cmdbuff == NULL || cmd == NULL)
return;

char pathname[128] = {"/home/jiege/mybash/bin/"};
strcat(pathname, cmdbuff[0]);

//判断是前台/后台运行
int ret = IsBack(cmd); //默认前台0


pid_t pid = fork();
if(pid == -1)
{
perror("fork err");
return;
}

if(pid == 0)
{
execv(pathname, cmdbuff);
perror("command not found");
exit(0);
}
else
{
//前台运行
if(ret == 0)
{
wait(NULL);
}
//后台运行通过注册的信号zombie去处理僵尸进程
}
}
//处理后台的僵尸进程
void Zombie()
{
wait(NULL);
}

//判断是否在后台运行
int IsBack(const char* cmd)
{
if(cmd == NULL)
return -1;
if(strstr(cmd, "&") != NULL)
return 1;
return 0;
}

//切换目录
void Mycd(const char* path)
{
if(path == NULL)
return;
if(chdir(path) == -1)
{
perror("cd err");
}
}
  • main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "mybash.h"

int main()
{
//处理后台的僵尸进程
signal(SIGCHLD, Zombie);

char* cmd = NULL;
while(1)
{
//输出提示符信息
OutPutInfo();
//从键盘获取命令
cmd = GetCmd();
//存放解析后的参数
char* cmdbuff[32] = {0};
//命令解析
returncode ret = CmdResolve(cmd, cmdbuff);
if(ret == EXIT)
{
free(cmd);
cmd = NULL;
break;
}
else if(ret == MYCD)
{
//切换路径
Mycd(cmdbuff[1]);
free(cmd);
cmd = NULL;
}
//空地址
else if(ret == ADDR_NULL)
{
free(cmd);
cmd = NULL;
continue;
}
else
{
//进程替换
ChildExec(cmdbuff, cmd);
}

}

return 0;
}
  • makefile
1
2
3
4
5
6
7
8
9
10
objects=main.o mybash.o 

mybash:$(objects)
gcc -o mybash $(objects)

$(objects): mybash.h main.c mybash.c

.PHONY:clean
clean:
rm -f mybash $(objects)

(三)最终项目文件结构

在这里插入图片描述

源码已上传githubhttps://github.com/HuTaoHub/mybash.git