承接上文Linux 进程的创建、终止、等待与程序替换保姆级讲解-CSDN博客,涉及所用到的代码,本文所绑定的资源就是上篇文章的主要代码。
完整代码在文章末尾
目录
1.实现编写代码输出一个命令行
a.如何获取自己的用户名,主机名,路径名?
b.ubuntu的HOSTNAME的获取方法:
c.完整代码(改前)printf进行打印的时候数据存在缓冲区:
2.实现编写代码获取用户命令字符串
a.“ls -a -l -i”本质上是一个字符串,使用 fgets() 获取一整个字符串
b.注意当在echo :%d后加换行符:
3.分割获取的用户命令字符串
n.创建子进程执行命令
shell 1.0 代码,程序只能运行一次
n + 1:将命令多次执行
shell 2.0 需补坑完整代码:
nn:填补上述shell代码的坑(cd无用的问题)
原因:
4.检查命令是否是内建命令(只有bash能执行的命令)
chdir()更改当前的工作路径
getcwd()获取进程当前工作目录的绝对路径
5.将命令行路径通过使用绝对路径改为相对路径
为什么定义宏,以及使用do{}while(0)?
6.内建命令echo $?问题
7.自定义环境变量export HELLO=12345
Export()函数
a.使用strdup()函数复制gArgv[1],arg指向字符串首地址,避免修改原始命令字符串
b. 使用strchr()函数查询是否有 = ,如果有则返回 = 的地址,没有则返回NULL
c.判断是否为NULL,如果为空说明export使用的格式错误
d.再将*eq指向的 = ,位置置为\0,截取arg = 前的字符串,eq+1,得到 = 后面的字符串。
e.setenv()设置环境变量:
2. echo $HELLO 的时候需要将$HELLO替换成它对应的值12345,从而输出12345
a.添加变量替换函数 ReplaceEnvVars():
1.上文所写到的程序可以执行系统的所有命令,包括自己写的可执行程序。
2. 在执行命令的时候,只执行了一次就结束,本篇文章主要讲如何让程序不断地执行不同的命令(可执行程序) -----> shell ---> 模拟实现命令行
实际上我们所看到的简单的命令行,本质上是一个字符串,并且我们输入的命令也是字符串。将读进来的字符串进行分析,解析成命令,再fork(), 再exec, 这条命令就执行了。
pupu@VM-8-15-ubuntu:~/bitclass/class_20/myshell$
bash 本质上是一个进程,有独立的pid
显示进程列表的表头,以及列出
bash
进程信息,并且过滤掉grep bash自身进程:ps ajx | head -1 && ps ajx | grep bash | grep -v grep
得到:
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 1366539 1366540 1366540 1366540 pts/0 1368748 Ss 1002 0:00 -bash 1372289 1372290 1372290 1372290 pts/1 1386639 Ss 1002 0:00 -bash
以上算本文的周边笔记知识提及。
1.实现编写代码输出一个命令行
a.如何获取自己的用户名,主机名,路径名?
环境变量可以通过函数getenv() 头文件<stdlib.h>来获取,获取自己的用户名,主机名,路径名从环境变量(命令行输入env)里定向获取。
查取到用户名为
LOGNAME=pupu
测试获取登录名:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>#define SIZE 512const char *getusername()
{const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name;
}int main()
{//1.我们需要自己输出一个命令行char output[SIZE];printf("name: %s\n",getusername());return 0;
}
输出结果:获取成功
我只想要当前路径,往往PWD中所存储的是绝对路径,如何截取字符串获得当前路径(可以定义尾指针,到 ' / '截取停止,请看目录5.将命令行路径改为相对路径)
PWD=/home/pupu/bitclass/class_20/myshell
ubuntu系统环境变量中默认没有HOSTNAME,centos系统环境变量中可以直接通过env查取到,可以用类似于获取用户名的方式来做:
b.ubuntu的HOSTNAME的获取方法:
const char *GetHostName() {char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none"; }
此时,我的代码存在 内存作用域问题:
buffer
是局部数组,在函数返回后其内存会被释放,导致返回的hostname
指针成为 悬垂指针,访问时可能输出随机内容或截断的字符串。
解决办法:
将 buffer 声明为 static(处于静态存储区) , 延长其生命周期至程序结束:
const char *GetHostName() {static char buffer[512]; // 静态存储期,函数返回后内存仍有效if (gethostname(buffer, sizeof(buffer)) == 0) {buffer[sizeof(buffer)-1] = '\0'; // 确保字符串终止return buffer;}return "none"; }
此时便能获取到正确的hostname了。
c.完整代码(改前)printf进行打印的时候数据存在缓冲区:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<stdlib.h>#define SIZE 512const char *GetUserName() {const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name; }const char *GetHostName() {static char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none"; }//获取当前的路径 const char *GetCwd() {const char *cwd = getenv("PWD");if(cwd == NULL) return "none";return cwd; }void MakeCommendLine(char line[], size_t size) {//获取三个字符串const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();//拼接字符串snprintf(line, size, "%s@%s:%s^_^ -> ", username, hostname, cwd); }int main() {//1.我们需要自己输出一个命令行char commendline[SIZE];MakeCommendLine(commendline, sizeof(commendline));printf("%s", commendline);sleep(5);return 0; }
但是在运行程序的时候会发现,我们想要的字符串会等上5s才打印出来,这是因为printf在进行打印的时候数据是会写在缓冲区中的,当程序结束时才会出来,这里的想法是将制作命令行与打印命令行放进一个函数里,并使用fflush(stdout),刷新标准输出流
stdout
,
将缓冲区中的输出数据立即写到输出设备:
此时运行代码:就会直接先打印出我们制作的命令行。延迟5s的原因是为了能够看到这个效果:
2.实现编写代码获取用户命令字符串
a.“ls -a -l -i”本质上是一个字符串,使用 fgets() 获取一整个字符串
char *fgets(char *s, int size, FILE *stream);
按行从特定的文件流当中获取指定的内容,成功获取字符串时,返回的是获取到的字符串的起始地址,失败则返回none。
如图:为了使代码更具有可读性
运行此代码进行测试:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<stdlib.h>#define SIZE 512const char *GetUserName() {const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name; }const char *GetHostName() {static char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none"; }//获取当前的路径 const char *GetCwd() {const char *cwd = getenv("PWD");if(cwd == NULL) return "none";//暂时这样写,后续会修改return cwd; }void MakeCommendLineAndPrint() {//实现输出一个命令行char line[SIZE];//获取三个字符串const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();//拼接字符串snprintf(line, sizeof(line), "%s@%s:%s^_^ -> ", username, hostname, cwd);printf("%s", line);fflush(stdout); }int main() {//自己输出一个命令行MakeCommendLineAndPrint();//获取用户命令字符串//1.再定一个缓冲区char usercommend[SIZE];//2.获取:从标准输入流中获取char *s = fgets(usercommend, sizeof usercommend, stdin);//起到一个输入停留的作用if(s == NULL) return 1; printf("echo : %s", usercommend);return 0; }
输出结果:
b.注意当在echo :%d后加换行符:
原代码并没有加\n,当加上\n后按理来说只会多一个空行,这里却空了两行相当于有两个\n?是为什么:因为,在我输完ls -a -l之后还摁了回车,回车符也被读入\r\n。
修改:
运行代码:此时就正常打印出
为了使代码具有可读性,我封装获取命令字符串的代码:
3.分割获取的用户命令字符串
a.封装一个函数SplitCommend()用于分割命令行字符串,创建一个全局变量的表gArgv[NUM],#define NUM 32,分隔符:define SEP " "
需要做到的是:将"ls -a -l -n" ----> "ls", "-a", "-l", "-n"
使用strtok函数,将一个子串,按照指定的分隔符进行分割,返回值就是从左往右分割出的第一个字符,第一次调用时把字符串保存下来,将这个位置设置为NULL,第二次调用就会对历史字符串继续分割,最后为NULL的时候,就结束了。
char *strtok(char *str, const char *delim);
define SEP " " 请注意,分隔符得设置成字符串才能传进去,不能是' ' 字符。
请阅读下面我修改后代码,对代码的提示:
运行代码:此时已将字符分割存入表内
n.创建子进程执行命令
只能让子进程去执行具体原因参见我的上一篇博客进程替换部分:Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解-CSDN博客
图中就是我对代码进行的修改
运行结果:已经成功,再删除多余代码就行。
将函数封装,删去多余代码
运行结果:
shell 1.0 代码,程序只能运行一次
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<stdlib.h> #include<string.h> #include<errno.h>#define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32void Die() {exit(1); }const char *GetUserName() {const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name; }const char *GetHostName() {static char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none"; }//获取当前的路径 const char *GetCwd() {const char *cwd = getenv("PWD");if(cwd == NULL) return "none";return cwd; }void MakeCommendLineAndPrint() {//实现输出一个命令行char line[SIZE];//获取三个字符串const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();//拼接字符串snprintf(line, sizeof(line), "%s@%s:%s^_^ -> ", username, hostname, cwd);printf("%s", line);fflush(stdout); }//获取用户命令字符串 int GetUserCommend(char commend[], size_t n) {//2.再定一个缓冲区//2.1.获取:从标准输入流中获取char *s = fgets(commend, n, stdin);if(s == NULL) return -1; commend[strlen(commend) - 1] = ZERO;return strlen(commend); } //定义一张全局的表 char *gArgv[NUM];void SplitCommend(char commend[], size_t n) {//"ls -a -l -n" ---> "ls", "-a", "-l", "-n"gArgv[0] = strtok(commend, SEP);int index = 1;while(gArgv[index++] = strtok(NULL, SEP));//故意写成 = ,表示先赋值,再判断。刚好让gArgv最后一个元素是NULL,并且while判断结束 }void ExecuteCommend() {//n.执行命令:pid_t id = fork();//创建的子进程失败if(id < 0) Die();else if(id == 0){//childexecvp(gArgv[0],gArgv);exit(errno);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id){// printf("father wait success!, child exit code : %d\n", WEXITSTATUS(status));}} }int main() {//1.自己输出一个命令行MakeCommendLineAndPrint();//2.获取用户命令字符串char usercommend[SIZE];int n = GetUserCommend(usercommend, sizeof(usercommend));if(n <= 0) return 1;//printf("echo : %s\n", usercommend);//3.命令行字符串分割SplitCommend(usercommend, sizeof(usercommend));for(int i = 0; gArgv[i]; i++)//当i = 最后一个字符串的下标时,因为为NULL,所以退出循环{// printf("gArgv[%d]: %s\n", i, gArgv[i]);}//执行命令ExecuteCommend();return 0; }
以上我所写的shell只能跑一次,想要像真正的命令行一样就需要可以执行多次。
n + 1:将命令多次执行
while 循环,不退出就能一直执行:
运行结果:
以上就是一个简单shell的制作。
shell 2.0 需补坑完整代码:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<stdlib.h> #include<string.h> #include<errno.h>#define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32void Die() {exit(1); }const char *GetUserName() {const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name; }const char *GetHostName() {static char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none"; }//获取当前的路径 const char *GetCwd() {const char *cwd = getenv("PWD");if(cwd == NULL) return "none";return cwd; }void MakeCommendLineAndPrint() {//实现输出一个命令行char line[SIZE];//获取三个字符串const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();//拼接字符串snprintf(line, sizeof(line), "%s@%s:%s^_^ -> ", username, hostname, cwd);printf("%s", line);fflush(stdout); }//获取用户命令字符串 int GetUserCommend(char commend[], size_t n) {//2.再定一个缓冲区//2.1.获取:从标准输入流中获取char *s = fgets(commend, n, stdin);if(s == NULL) return -1; commend[strlen(commend) - 1] = ZERO;return strlen(commend); } //定义一张全局的表 char *gArgv[NUM];void SplitCommend(char commend[], size_t n) {//"ls -a -l -n" ---> "ls", "-a", "-l", "-n"gArgv[0] = strtok(commend, SEP);int index = 1;while(gArgv[index++] = strtok(NULL, SEP));//故意写成 = ,表示先赋值,再判断。刚好让gArgv最后一个元素是NULL,并且while判断结束 }void ExecuteCommend() {//n.执行命令:pid_t id = fork();//创建的子进程失败if(id < 0) Die();else if(id == 0){//childexecvp(gArgv[0],gArgv);exit(errno);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id){// printf("father wait success!, child exit code : %d\n", WEXITSTATUS(status));}} }int main() {int quit = 0;while(!quit){//1.自己输出一个命令行MakeCommendLineAndPrint();//2.获取用户命令字符串char usercommend[SIZE];int n = GetUserCommend(usercommend, sizeof(usercommend));if(n <= 0) return 1;//printf("echo : %s\n", usercommend);//3.命令行字符串分割SplitCommend(usercommend, sizeof(usercommend));for(int i = 0; gArgv[i]; i++)//当i = 最后一个字符串的下标时,因为为NULL,所以退出循环{// printf("gArgv[%d]: %s\n", i, gArgv[i]);}//执行命令ExecuteCommend();}return 0; }
nn:填补上述shell代码的坑(cd无用的问题)
我们当前的shell无法进行路径的切换:
每个进程都会记录当前所属的路径,所以父进程有,子进程有。
原因:
我的shell中子进程进行cd ..,和父进程没有关系,也就是和bash没有关系,因此不会切换。
因此当需执行的命令是 cd 时,应该让此命令给父进程执行 -----> cd是内建命令
4.检查命令是否是内建命令(只有bash能执行的命令)
chdir()更改当前的工作路径
这里需要使用到chdir()系统调用命令,<unistd.h>, 用于更改当前的工作路径
int chdir(const char *path);
更改我当前的工作路径:
cd 命令一般只有 cd(进入家目录), cd 相对路径/绝对路径(进入路径所处目录),cd ..(进入上级目录),cd ~(进入家目录),cd -(打印上级目录并进入上级目录,在这里没有写)
因此gArgv[1],便是cd的选择命令,如果为空,则进入家目录,不为空就可以直接使用,直接调用系统命令,更改当前的工作路径。更改成功,但是依然存在问题:命令行中的路径始终未发生改变
因此我们再次更改代码,将输入的有效路径传给cwd,并更新环境变量:
运行结果:
这是因为,此时我将获取到的字符串直接给cwd了,并且还更新了环境变量导致PWD="path",当我输入 ..,那么PWD=..,因此我们需要得到当前工作目录的绝对路径,再将他的值传给cwd,更新环境变量。
getcwd()获取进程当前工作目录的绝对路径
这告诉我们,每次刷新命令行路径的时候也需要采用绝对路径,使用系统调用命令getcwd()
char *getcwd(char *buf, size_t size);
这里使用temp[SIZE*2]用于存储获得的绝对路径
运行结果:因为我定义的cwd[SIZE*2] -->1024个字节,PWD+%s --->1028个字节,超出范围。
因此我直接:
运行结果:完全正确
5.将命令行路径通过使用绝对路径改为相对路径
在centos系统之下,命令行路径只会记录当前的相对路径:
因此就需要我们对路径进行剪切:
定义一个宏函数(解释:看目录)
运行结果:
此时还不够完美,其中还有 ' / ',这是因为指针指向/停止,将 / 的地址传回来,因此直接对cwd + 1就可以:
运行结果:
当到达根目录时,却没有路径字符串了
再修改:
运行结果:
为什么定义宏,以及使用do{}while(0)?
#define SkipPath(p) do{ p += (strlen(p)- 1); while(*p != '/') p--; }while(0)
首先,这里涉及到对指针做操作,如果我想封装一个函数对这个指针操作,那就需要传二级指针,因此我们用宏,使cwd -被替换成--> p ,do{}while(0)形成代码块,并且do{}while()后面可以随便带‘ ;‘,方便后续的使用:就很像一个函数了,特别是需要写在 if 里面,也不会出什么错。
当在写宏函数需要用代码块的的时候建议写在do{}while()里面(编码小技巧)
echo $?,返回最后一次进程的返回值(退出码 ):
运行结果:
6.内建命令echo $?问题
图片里为什么还要把lastcode --> 0 不懂可以看:Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解-CSDN博客
运行结果:
7.自定义环境变量export HELLO=12345
当我们导入环境变量的时候:
1. export HELLO=12345,又需要识别到是内建命令,通过strcmp来判断。
创建函数Export()来执行此代码:
Export()函数
a.
使用strdup()函数复制gArgv[1],arg指向字符串首地址
,避免修改原始命令字符串char *arg = strdup(gArgv[1]);
b. 使用strchr()函数查询是否有 = ,如果有则返回 = 的地址,没有则返回NULL
char *eq = strchr(arg, '=');
c.判断是否为NULL,如果为空说明export使用的格式错误
if (eq == NULL) {fprintf(stderr, "export: invalid format\n");free(arg); // 错误分支也要释放内存return;}
d.再将*eq指向的 = ,位置置为\0,截取arg = 前的字符串,eq+1,得到 = 后面的字符串。
*eq = '\0';
e.setenv()设置环境变量:
#include <stdlib.h> // 需要包含头文件int setenv(const char *name, const char *value, int overwrite);
参数:
name
:环境变量名(如"PATH"
)|value
:要设置的值(如"/usr/bin"
)。
overwrite
:1
(非零):若变量已存在,则覆盖旧值。0
:若变量已存在,则保留旧值,不修改。返回值:
0
:成功。-1
:失败(错误原因存于errno
,如ENOMEM
内存不足)。将(arg = HELLO,eq = 12345,1-->确认覆盖),将环境变量名为HELLO的值确认使用12345覆盖。如果原本这个环境变量不存在,则在env中添加这个新的环境变量。并且判断是否创建,执行成功。
if (setenv(arg, eq+1, 1) != 0) {perror("export");}
最后释放arg所指向的空间
free(arg); // 正常路径释放内存
2. echo $HELLO 的时候需要将$HELLO替换成它对应的值12345,从而输出12345
执行
echo $HHH
时,Shell 本应进行以下操作:
变量替换:将
$HHH
替换为环境变量HHH
的值。执行命令:调用
echo
并传入替换后的参数。a.添加变量替换函数 ReplaceEnvVars():
如果$后跟的是?就直接使用前面写的获取退出码的那个代码,这里要排除一下
void ReplaceEnvVars() {for (int i = 0; gArgv[i] != NULL; i++){if(gArgv[i][0] == '$'){if(gArgv[i][1] != '?')//?就直接使用前面写的获取退出码的那个代码,这里要排除一下{char *var_name = gArgv[i] + 1; //跳过'$',获取变量名char *value = getenv(var_name);if(value){//如果这个变量名已经在环境变量中存在gArgv[i] = strdup(value);}else{gArgv[i] = strdup("");}}}} }
将这个函数在调用判断内建命令的函数前进行调用:
运行代码:
以上就是shell的模拟实现。
完整代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{ p += (strlen(p)- 1); while(*p != '/') p--; }while(0)char cwd[SIZE*4];//定义一张全局的表
char *gArgv[NUM];int lastcode = 0;void Die()
{exit(1);
}const char *GetHome()
{const char *home = getenv("HOME");if(home == NULL) return "/";return home;
}const char *GetUserName()
{const char *name = getenv("LOGNAME");if(name == NULL) return "none";return name;
}const char *GetHostName()
{static char buffer[256];char *hostname = buffer;if (gethostname(hostname, sizeof(buffer)) == 0) return hostname;return "none";
}//获取当前的路径
const char *GetCwd()
{const char *cwd = getenv("PWD");if(cwd == NULL) return "none";return cwd;
}void MakeCommendLineAndPrint()
{//实现输出一个命令行char line[SIZE];//获取三个字符串const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();SkipPath(cwd);//拼接字符串snprintf(line, sizeof(line), "%s@%s:%s^_^ -> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1);printf("%s", line);fflush(stdout);
}//获取用户命令字符串
int GetUserCommend(char commend[], size_t n)
{//2.再定一个缓冲区//2.1.获取:从标准输入流中获取char *s = fgets(commend, n, stdin);if(s == NULL) return -1; commend[strlen(commend) - 1] = ZERO;return strlen(commend);
}void SplitCommend(char commend[], size_t n)
{//"ls -a -l -n" ---> "ls", "-a", "-l", "-n"gArgv[0] = strtok(commend, SEP);int index = 1;while(gArgv[index++] = strtok(NULL, SEP));//故意写成 = ,表示先赋值,再判断。刚好让gArgv最后一个元素是NULL,并且while判断结束
}void ExecuteCommend()
{//n.执行命令:pid_t id = fork();//创建的子进程失败if(id < 0) Die();else if(id == 0){//childexecvp(gArgv[0],gArgv);exit(errno);}else{int status = 0; pid_t rid = waitpid(id, &status, 0);if(rid == id){// printf("father wait success!, child exit code : %d\n", WEXITSTATUS(status));lastcode = WEXITSTATUS(status);if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode); }}
}//判断是哪一种cd
void Cd()
{const char* path = gArgv[1];if(path == NULL) path = GetHome();//PATH 一定存在chdir(path);//刷新环境变量char temp[SIZE*2];getcwd(temp, sizeof(temp));snprintf(cwd, sizeof(cwd),"PWD=%s", temp);//将cwd设置为一个全局变量,实时更新putenv(cwd); //再更新环境变量
}void ReplaceEnvVars()
{for (int i = 0; gArgv[i] != NULL; i++){if(gArgv[i][0] == '$'){if(gArgv[i][1] != '?'){char *var_name = gArgv[i] + 1; //跳过'$',获取变量名char *value = getenv(var_name);if(value){//如果这个变量名已经在环境变量中存在gArgv[i] = strdup(value);}else{gArgv[i] = strdup("");}}}}
}void Export()
{if(!gArgv[1]){fprintf(stderr, "export: missing argument!\n");return;}char *arg = strdup(gArgv[1]);char *eq = strchr(arg,'=');//查找是否有 = ,如果有就返回 = 的地址if(eq == NULL){fprintf(stderr, "export: invalid format\n");free(arg);return;}*eq = '\0';//将 = 的位置的字符置为\0提前结束if(setenv(arg, eq+1, 1) != 0){perror("export");}free(arg);
}//检查是否是内建命令
int CheckBuildin()
{int yesorno = 0;const char *enter_cmd = gArgv[0];if(strcmp(enter_cmd, "cd") == 0){yesorno = 1; Cd();}else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1],"$?") == 0){yesorno = 1;printf("%d\n", lastcode);lastcode = 0;}else if(strcmp(enter_cmd, "export") == 0){Export();yesorno = 1;}return yesorno;
}int main()
{int quit = 0;while(!quit){//1.自己输出一个命令行MakeCommendLineAndPrint();//2.获取用户命令字符串char usercommend[SIZE];int n = GetUserCommend(usercommend, sizeof(usercommend));if(n <= 0) return 1;//printf("echo : %s\n", usercommend);//3.命令行字符串分割SplitCommend(usercommend, sizeof(usercommend));for(int i = 0; gArgv[i]; i++)//当i = 最后一个字符串的下标时,因为为NULL,所以退出循环{// printf("gArgv[%d]: %s\n", i, gArgv[i]);}ReplaceEnvVars();//4,检查是否是内建命令n = CheckBuildin();if(n) continue;//执行命令ExecuteCommend();}return 0; }
结语:
随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。
在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。
你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。