如果学习了常见的Linux命令,exec*函数,环境变量,那你就可以尝试一下写一个简单的shell;
下面跟着我的步骤来吧!!🤩🤩
输入命令行
既然要写一个shell,我们第一步先把这个输入命令行打印出来:
观察一下:命令行中有三个环境变量,我们如何查找它们呢?
这就需要使用env查找:发现USER、HOSTNAME、PWD;
怎么用代码来获取这三个环境变量对应的值呢?
用:getenv函数
有关函数更详细的内容可以区man手册上区查看
接下来,我们就可以写代码了:(主要代码,完整代码在文章最后)
void Makecommandline()
{
char cmd[SIZE];
const char *name=Getname();
const char *hostname = Gethostname();
const char *cwd =Getcwd();
snprintf(cmd,sizeof(cmd),"[%s@%s %s]> ",name,hostname,cwd);
printf("%s",cmd);
fflush(stdout);
}
获取用户命令
想一下,我们输入一个命令时,有时候是ls,有时候是ls -l -a ,输入的有空格怎么办呢?🤖
这时就可以用fgets函数来输入命令;
void Getusercommand(char Usercmd[])
{
char *s = fgets(Usercmd,sizeof(Usercmd),stdin);
if(s==NULL)
{
perror("fgets");
exit(1);
}
Usercmd[strlen(Usercmd)-1]='\0';
}
命令行字符串分割
获取到用户命令后,接下来我们要对这个命令进行分割:
分割用的是strtok函数;
代码:
void Splitcommand(char Usercmd[])
{
argv[0]=strtok(Usercmd,SEP);
int index=1;
while(argv[index++]=strtok(NULL,SEP));
}
执行命令
完成上述的准备工作后,我们要执行我们输入的命令,那要怎么执行呢?
🤡当然是用的我们的exec*函数喽,这里根据实际情况,我们选择的应该是execvp()函数;
💥💥注意:我们使用exec*函数时,要创建一个子进程来进行,这样才不会使父进程中后续代码被覆盖!!
代码:
void Executecommand()
{
pid_t id =fork();
if(id<0)
{
exit(1);
}
else if(id==0)
{
execvp(argv[0],argv);
exit(errno);
}
else
{
int status=0;
pid_t rid =waitpid(id,&status,0);
if(rid>0)
{
//wait sucess
lastcode = WEXITSTATUS(status);
if(lastcode!=0)
{
printf("%s:%s:%d\n",argv[0],strerror(lastcode),lastcode);
}
}
}
}
检测命令是否是内建命令
😺😺完成上述的四部后,你就已经完成了一个简单的shell,但是并不完整;哪里不完整呢?我们可以输入一个内建命令,比如cd命令,这时我们就找到了要完善的地方;
怎么完善呢?
我们要判断一下这个命令是不是内建命令,怎么判断呢,非常简单,直接if语句:
如果有其他的内建命令,直接添加即可;
Cd():如果是内建cd命令,看一下argv[1]是不是空,
(1)如果是空,我们输入的命令就是cd,那不就是直接回到家目录了嘛,如果不为空,就不要变了;
(2)接下来,直接改变cwd即可,用chdir函数;
(3)修改环境变量PWD的值,用putenv函数;
💥💥注意:putenv
用于将一个字符串添加到环境变量中,或者修改已经存在的环境变量。这个字符串的格式通常是 "NAME=VALUE"
,其中 NAME
是环境变量的名称,VALUE
是其对应的值。
你已经完成了一个基础的shell,更完善的shell还会继续更新!!
完整代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #include<sys/types.h>
6 #include<sys/wait.h>
7 #include<errno.h>
8
9 # define SkipPath(p) do{p+=(strlen(p)-1);while(*p!='/')p--;}while(0)
10
11
12 #define SIZE 512
13 #define SEP " "
14
15
16 char cwd[SIZE*2];
17 int lastcode =0;
18 char *argv[SIZE*2];
19
20 const char *Getname()
21 {
22 const char *name=getenv("USER");
23 if(name==NULL)return "None";
24 return name;
25 }
26
27
28 const char *Gethostname()
29 {
30 const char *hostname=getenv("HOSTNAME");
31 if(hostname==NULL)return "None";
32 return hostname;
33 }
34
35
36 const char *Getcwd()
37 {
38 const char *pwd=getenv("PWD");
39 if(pwd==NULL)return "None";
40 return pwd;
41 }
42
43 void Makecommandline()
44 {
45 char cmd[SIZE];
46 const char *name=Getname();
47 const char *hostname = Gethostname();
48 const char *cwd =Getcwd();
49 SkipPath(cwd);
50 snprintf(cmd,sizeof(cmd),"[%s@%s %s]> ",name,hostname,strlen(cwd)==1 ? "/":cwd+1);
51 printf("%s",cmd);
52 fflush(stdout);
53 }
54
55 void Getusercommand(char Usercmd[])
56 {
57 char *s = fgets(Usercmd,sizeof(Usercmd),stdin);
58 if(s==NULL)
59 {
60 perror("fgets");
61 exit(1);
62 }
63 Usercmd[strlen(Usercmd)-1]='\0';
64 }
65
66 void Splitcommand(char Usercmd[])
67 {
68 argv[0]=strtok(Usercmd,SEP);
69 int index=1;
70 while(argv[index++]=strtok(NULL,SEP));
71 }
72 const char *Gethome()
73 {
74 const char *home =getenv("HOME");
75 if(home==NULL)return "/root";
76 return home;
77 }
78 void Cd()
79 {
80 const char *path =argv[1];
81 if(path==NULL)
82 {
83 //返回家目录
84 path = Gethome();
85 }
86 chdir(path);
87 char tmp[SIZE*2];
88 getcwd(tmp,sizeof(tmp));
89 snprintf(cwd,sizeof(cwd),"PWD=%s",tmp);
90 putenv(cwd);
91 //printf("%s\n",cwd);
92 }
93 int Checkbuildin()
94 {
95 int yes=0;
96 if(strcmp(argv[0],"cd")==0)
97 {
98 yes=1;
99 Cd();
100 }
101 return yes;
102 }
103 void Executecommand()
104 {
105 pid_t id =fork();
106 if(id<0)
107 {
108 exit(1);
109 }
110 else if(id==0)
111 {
112 execvp(argv[0],argv);
113 exit(errno);
114 }
115 else
116 {
117 int status=0;
118 pid_t rid =waitpid(id,&status,0);
119 if(rid>0)
120 {
121 //wait sucess
122 lastcode = WEXITSTATUS(status);
123 if(lastcode!=0)
124 {
125 printf("%s:%s:%d\n",argv[0],strerror(lastcode),lastcode);
126 }
127 }
128 }
129 }
130 int main()
131 {
132 while(1)
133 {
134 //1、我们自己输入一个命令行
135 Makecommandline();
136
137 //2、获取用户命令字符串分割
138 char Usercmd[SIZE];
139 Getusercommand(Usercmd);
140 //printf("%s\n",Usercmd);
141 //3、命令行字符串分割
142 Splitcommand(Usercmd);
143 //4、检测命令是否是内建命令
144 int n = Checkbuildin();
145 if(n)continue;
146 //5、执行命令
147 Executecommand();
148 }
149 return 0;
150 }