仿写bash

仿写bash程序

  • 1、输出一些提示符信息:[stu@host Desktop]$
  • 2、等待用户输入命令
  • 3、完成简单的解析 (对命令进行简单分析:没有输入命令 内置命令(cd exit)外置ls)

外置ls:

子进程通过exec替换成用户输入的命令对应的程序

父进程等待命令执行结束–》前台执行

父进程继续输出提示信息,接收用户输入新命令–》后台执行

上代码

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
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/types.h>
#include<pwd.h>
#include<sys/utsname.h>
#define CMDLEN 128
#define NUM 20
//[用户名@主机名 当前工作目录]标识符
void print_info()
{
struct passwd *pw=getpwuid(getuid());
assert(pw!=NULL);
struct utsname host;
uname(&host);
char path[CMDLEN]={0};
getcwd(path,CMDLEN-1);//绝对路径
char *dirname=NULL;
if(strcmp(path,pw->pw_dir)==0)
{
dirname="~"; //在家目录下
}
else
{
dirname=path+strlen(path);
while(*dirname!='/')
{
dirname--;
}
if(strlen(path)!=1)
{
dirname++;
}
}

char flag='$';
if(getuid()==0)//0是root用户的ID
{
flag='#';
}
printf("[%s@%s %s]%c ",pw->pw_name,host.nodename,dirname,flag);
}
//将命令与参数分割,分别存储到指针数组
void cut_command(char *cmd,char *cmdarr[])
{
char *p=strtok(cmd," ");
int index=0;
while(p!=NULL&&index<NUM)
{
cmdarr[index++]=p;
p=strtok(NULL," ");
}
}
void mycd(char *path)
{
//cd cd ~切换到家目录
//cd - 切换到上一次所在位置
//cd path
static char oldpwd[CMDLEN]={0};
if(path==NULL||strncmp(path,"~",1)==0)
{
//切换到家目录
struct passwd *pw=getpwuid(getuid());
assert(pw!=NULL);
path=pw->pw_dir;
}
else if(strncmp(path,"-",1)==0)
{
//切换到上次位置
if(strlen(oldpwd)==0)
{
printf("cd error,oldpwd not set\n");
return ;
}
path=oldpwd;
}
char nowpwd[CMDLEN]={0};
getcwd(nowpwd,CMDLEN-1);
if(chdir(path)==-1)
{
perror("cd ");
return;
}
memset(oldpwd,0,CMDLEN);
strcpy(oldpwd,nowpwd);
}
void deal_exec(char *cmdarr[])
{
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)
{
//用户给定的一个命令 ls pwd su ps
//用户想执行一个程序 ./main ./mybash
char file[CMDLEN]={0};
if(strstr(cmdarr[0],"/")!=NULL)
{
strcpy(file,cmdarr[0]);
}
else
{
strcpy(file,"/bin/");
strcat(file,cmdarr[0]);
}
execv(file,cmdarr); //如果命令不存在,则execv会失败
perror("execv ");
exit(0); //如果替换失败,子进程要能正常结束
}
else
{
//前后台处理;
wait(NULL);
}

}
int main()
{
printf("mybash started\n");
while(1)
{
//1、打印提示信息
print_info();
//获取用户输入的命令
char cmd[CMDLEN]={0};
fgets(cmd,127,stdin);//最后的回车符也在cmd数组中
cmd[strlen(cmd)-1]=0;
if(strlen(cmd)==0)
{
continue;
}
//命令与各参数的分割 ls -a -l -i
char *cmdarr[NUM]={0};
cut_command(cmd,cmdarr);
//3、划分命令 空 内置 外置
if(strlen(cmdarr[0])==2&&strncmp(cmdarr[0],"cd",2)==0)
{
mycd(cmdarr[1]);
}
else if(strlen(cmdarr[0])==4&&strncmp(cmdarr[0],"exit",4)==0)
{
printf("my bash end\n");
exit(0);//结束进程
}
else//外置命令
{
deal_exec(cmdarr);
}
}
}

测试

建立一个空目录mybin,存放自己的命令实现。然后在处理外部命令那块把路径换成mybin文件的目录

首先实现pwd命令

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
int main()
{
char path[128]={0};
getcwd(path,127);
printf("%s\n",path);
exit(0);
}

myls实现

  • ls 默认显示当前工作路径下的文件
  • ls 路径 显示指定路径下的文件
1
2
3
ls -a
ls -i
ls -l
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>

#define OPTION_A 0
#define OPTION_I 1
#define OPTION_L 2

#define SET_OPTION(option,val) (option)|=1<<(val)
#define IS_SET(option,val) (option)&(1<<(val))
int option=0;//按位来存储相关的选项值
//0 a
//1 i
//2 l

//解析用户输入的选项有哪些
void get_option(char *argv[],int argc)
{
int i=1;
for(;i<argc;++i)
{
if(strncmp(argv[i],"-",1)!=0)
{
continue;
}
if(strstr(argv[i],"a")!=NULL)
{
SET_OPTION(option,OPTION_A);
}
if(strstr(argv[i],"i")!=NULL)
{
SET_OPTION(option,OPTION_I);
}
if(strstr(argv[i],"l")!=NULL)
{
SET_OPTION(option,OPTION_L);
}
}
}
//打印文件类型
void print_filetype(mode_t file)
{
if(S_ISREG(file)) printf("-");//普通文件
else if(S_ISDIR(file)) printf("d");//目录文件
else if(S_ISCHR(file)) printf("c");//字符设备文件
else if(S_ISBLK(file)) printf("b");//块设备文件
else if(S_ISFIFO(file)) printf("p");//管道文件
else if(S_ISLNK(file)) printf("l");//链接文件
else printf("s");//socket文件
}
void print_filepower(mode_t file)
{
//USR权限
printf("%c",file&S_IRUSR?'r':'-');
printf("%c",file&S_IWUSR?'w':'-');
printf("%c",file&S_IXUSR?'x':'-');

//GRP权限
printf("%c",file&S_IRGRP?'r':'-');
printf("%c",file&S_IWGRP?'w':'-');
printf("%c",file&S_IXGRP?'x':'-');

//OTH权限
printf("%c",file&S_IROTH?'r':'-');
printf("%c",file&S_IWOTH?'w':'-');
printf("%c ",file&S_IXOTH?'x':'-');
}
void print_id(struct stat st)
{
struct passwd *pwd;
pwd=getpwuid(st.st_uid);
printf("%s ",pwd->pw_name);

struct group *grp;
grp=getgrgid(st.st_gid);
printf("%s ",grp->gr_name);
}
void print_ch_time(time_t mtime)
{
struct tm *t=localtime(&mtime);
printf("%2d月 %2d %02d:%02d ",t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
}
void print_filename(char *path,char *file)
{
char filename[128]={0};
strcpy(filename,path);
strcat(filename,"/");
strcat(filename,file);

struct stat st;
lstat(filename,&st);

if(S_ISREG(st.st_mode))//普通文件
{
//权限
if(st.st_mode&S_IXUSR||st.st_mode&S_IXGRP||st.st_mode&S_IXOTH)
{
printf("\033[1;32m%s\033[0m ",file);
}
else
{
printf("%s ",file);
}

}
else if(S_ISDIR(st.st_mode))
{
printf("\033[1;34m%s\033[0m ",file);
}
else if(S_ISLNK(st.st_mode))
{
printf("\033[1;44m%s\033[0m ",file);
}
else if(S_ISFIFO(st.st_mode))
{
printf("\033[40;33m%s\033[0m ",file);
}
else
{
printf("%s ",file);
}
}
//打印文件类型 权限 连接数 用户 组用户 大小 最后修改时间
void print_file_info(char *path,char *file)
{
char filename[128]={0};
strcpy(filename,path);
strcat(filename,"/");
strcat(filename,file);

struct stat st;
lstat(filename,&st);
printf("\n");
print_filetype(st.st_mode);//文件类型
print_filepower(st.st_mode);//文件权限
printf("%d ",st.st_nlink);//硬链接数
print_id(st);//用户id和组id
printf("%5ld ",st.st_size);//文件大小
print_ch_time(st.st_mtime);//最后修改时间
print_filename(path,file);
}
long long getblocks(char *path,char *file)
{
char filename[128]={0};
strcpy(filename,path);
strcat(filename,"/");
strcat(filename,file);

struct stat st;
lstat(filename,&st);

long long sum=0;
DIR *dir=opendir(path);
assert(dir!=NULL);

struct dirent *dt=NULL;
while(NULL!=(dt=readdir(dir)))
{
if(strncmp(dt->d_name,".",1)==0)
{
continue;
}
printf("%s ",dt->d_name);
sum+=st.st_blocks;
}
return sum;
}
void print_file(char *path)
{
DIR *dir=opendir(path);
assert(dir!=NULL);

struct dirent *dt=NULL;
int flag=1;
while(NULL!=(dt=readdir(dir)))
{
if(!(IS_SET(option,OPTION_A))&&strncmp(dt->d_name,".",1)==0)
{
continue;
}
if(IS_SET(option,OPTION_I))
{
printf("%d ",dt->d_ino);
}
if(IS_SET(option,OPTION_L))
{
if(flag)
{
printf("总用量 %lld",getblocks(path,dt->d_name));
flag=0;
}
print_file_info(path,dt->d_name);
continue;
}
print_filename(path,dt->d_name);
}
printf("\n");
closedir(dir);
}
int main(int argc,char *argv[])
{
get_option(argv,argc);
int flag=0;
int i=1;
for(;i<argc;++i)
{
if(strncmp(argv[i],"-",1)==0)
{
continue;
}
printf("%s :\n",argv[i]);
print_file(argv[i]);
flag=1;
}
if(!flag)
{
char path[128]={0};
getcwd(path,127);
print_file(path);
}
exit(0);
}

mysu

mybash->su->mybash

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
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<shadow.h>
#include<crypt.h>
#include<termios.h>
int main(int argc,char *argv[])
{
char *user="root";
if(argv[1]!=NULL)
{
user=argv[1];
}
printf("Password: ");
char password[128]={0};
struct termios oldter,newter;
tcgetattr(0,&oldter);
newter=oldter;
newter.c_lflag&=~ECHO;
tcsetattr(0,TCSANOW,&newter);
fgets(password,127,stdin);
tcsetattr(0,TCSANOW,&oldter);//又恢复回显功能
password[strlen(password)-1]=0;

struct spwd *sp=getspnam(user);
assert(sp!=NULL);
printf("%s\n",sp->sp_pwdp);

char salt[128]={0};
int index=0;
char *p=sp->sp_pwdp;
int count=0;
while(*p)
{
salt[index]=*p;
if(salt[index]=='$')
{
count++;
}
if(count==3)
{
break;
}
p++;index++;
}

char *mypasswd=(char *)crypt(password,salt);
if(strcmp(mypasswd,sp->sp_pwdp)!=0)
{
printf("password is error,plerse again\n");
exit(0);
}
pid_t pid=fork();
assert(pid!=-1);

if(pid==0)
{
struct passwd *pw=getpwnam(user);
assert(pw!=NULL);
setenv("HOME",pw->pw_dir,1);
setuid(pw->pw_uid);//真正的切换用户


execl(pw->pw_shell,pw->pw_shell,(char *)0);
perror(pw->pw_shell);
}
else
{
wait(NULL);
}

exit(0);
}

在使用的时候,将su的权限修改,在root下使用

1
2
chown root su
chmod s+s su

然后就可以执行su了