cpp
1 #include<cstdio>
2 #include<string.h>
3 #include<string>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cstdbool>
7 #include<sys/types.h>
8 #include<sys/wait.h>
9 #include<unistd.h>
10 #include<iostream>
11 #define command_size 1024
12 #define format "[%s@%s %s]#"
13 //下面定义shell的全局变量
14 char cwd[1024];
15 char cwdenv[1024];
16 using namespace std;
17 #define maxargc 128
18 char* my_argv[maxargc];
19 #define maxenv 2000
20 char* my_env[maxenv];
21 int lastcode=0;
22 int argc=0;
23 int myenvs=0;
24 const char* getusername()
25 {
26 const char* name=getenv("USER");
27 return name==NULL?"None":name;
28 }
29 const char* gethostname()
30 {
31 const char* hostname=getenv("HOSTNAME");
32 return hostname==NULL?"None":hostname;
33 }
34 const char* getpath()
35 {
36 const char* path=getcwd(cwd,sizeof(cwd));//获得当前进程的工作路径
37 if(path)
38 {
39 snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
40 putenv(cwdenv);
41 }
42 return path==NULL?"None":path;
43 }
44 const char* gethome()
45 {
46 const char* home=getenv("HOME");
47 return home==NULL?NULL:home;
48 }
49 void makecmdline(char cmd_prompt[],int size)
50 {
51 snprintf(cmd_prompt,size,format,getusername(),gethostname(),getpath());
52 }
53 void printcmdprompt()
54 {
55 char prompt[command_size];
56 makecmdline(prompt,sizeof(prompt));
57 printf("%s ",prompt);
58 fflush(stdout);
59 }
60 bool getcommandline(char* out,int size)
61 {
62 char* c=fgets(out,size,stdin);
63 if(c==NULL) return false;
64 out[strlen(out)-1]=0;
65 if(sizeof(out)==0) return false;
66 return true;
67 }
68 #define dsp " "
69 bool commandparse(char* commandline)
70 {
71 argc=0;
72 my_argv[argc++]=strtok(commandline,dsp);
73 while((bool)(my_argv[argc++]=strtok(NULL,dsp)));
74 argc--;
75 return argc>0?true:false;
76 }
77 int execute()
78 {
79 pid_t id=fork();
80 int status=0;
81 if(id==0)
82 {
83 //子进程
84 execvp(my_argv[0],my_argv);
85 exit(1);
86 }
87 pid_t rid=waitpid(id,&status,0);
88 if(rid>0)
89 {
90 lastcode=WEXITSTATUS(status);
91
92 }
93 return 0;
94 }
95 bool check()//查看是否是内建命令,并对内建命令进行相应的操作
96 {
97 string cmd=my_argv[0];
98 if(cmd=="cd") {
99 if(argc==1)//返回家目录
100 {
101 if(gethome()==NULL) return true;
102 else chdir(gethome());
103 }
104 else{
105 string where=my_argv[1];
106 chdir(where.c_str());
107 }
108 return true;
109 }
110 else if(cmd=="echo")
111 {
112 if(argc==2)
113 {
114 //echo "hello wworld"
115 //echo $?
116 //echo $PATH
117 string opt=my_argv[1];
118 if(opt=="$?")
119 {
120 cout<<lastcode<<endl;
121 lastcode=0;
122 }
123 else if(opt[0]=='$')//查找父进程的环境变量,因为我们今天并没有创建自己进程的环境变量表,所以我们还是使用从父进 程继承的环境变量
124 {
125 string envname=opt.substr(1);
126 if(getenv(envname.c_str())) cout<<getenv(envname.c_str())<<endl;
127 }
128 else
129 {
130 cout<<opt<<endl;
131 }
132 }
133 return true;
134 }
135 else if(cmd=="export")
136 {
137 if(argc==2)
138 {
139 my_env[myenvs]=(char*)malloc(strlen(my_argv[1])+1);
140 strcpy(my_env[myenvs++],my_argv[1]);//strcpy会拷贝字符串结尾的\0,但是strlen并不会将\0计算在内
141 putenv(my_env[myenvs-1]);
142 }
143 return true;
144 }
145 else return false;
146 }
147 void initenv()
148 {
149 extern char** environ;
150 memset(my_env,0,sizeof(my_env));
151 myenvs=0;
152 for(int i=0;environ[i];i++)
153 {
154 my_env[i]=(char*)malloc(strlen(environ[i])+1);
155 strcpy(my_env[i],environ[i]);
156 myenvs++;
157 }
158 my_env[myenvs++]=(char*)"test=haha";
159 my_env[myenvs]=NULL;
160
161 //导成环境变量
162 for(int i=0;my_env[i];i++)
163 {
164 putenv(my_env[i]);//换将变量中如果没有这个环境变量名字就新增这个环境变量,否则就将其替换。
165 }
166 }
167 int main()
168 {
169 //shell启动的时候要从系统中获取环境变量
170 //我们的环境变量的信息应该来原来父进程
171 initenv();
172 while(1)
173 {
174 //输出命令行提示符
175 printcmdprompt();
176 //获取用户输入的命令
177 char commandline[command_size];
178 if(!getcommandline(commandline,sizeof(commandline)))
179 continue;
180 //命令行分析
181 if(!commandparse(commandline)) continue;
182 //检查是否是内建命令
183 if(check()) continue;
184 //执行命令
185 execute();
186 }
187 return 0;
188 }
命令行解释器:
1.获取命令行
2.解析命令行
3.建立子进程
4.替换子进程
5.父进程等待子进程退出