目录
1.自定义Shell
1.打出对应命令行
1.样式上
2.第二版
3.第三版
命令行解析
执行命令
小调整
4.第四版
[chdir 更改工作路径 哪个进程调chdir就更改哪个路径](#chdir 更改工作路径 哪个进程调chdir就更改哪个路径)
内建命令
系统调用
5.echo
shell有两张表,一张命令行参数表,一张环境变量表
1.自定义Shell
user1@iZ5waahoxw3q2bZ myshell\]$
我们自己有个对应的字符串,shell启动时也可以打出类似形式的字符串
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.c
#include
int main()
{
printf("[user1@iZ5waahoxw3q2bZ myshell]#");
return 0;
}

**获取指定环境变量的值**
#include
char *getenv(const char *name);
### 1.打出对应命令行
#### 1.样式上
#[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.c
#include
#include
#include
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
int main()
{
printf("[%s@%s %s]#",GetUserName(),GetHostName(),GetPwd());
return 0;
}
> \[user1@iZ5waahoxw3q2bZ myshell\]$ ./myshell
>
> \[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell\]#\[user1@iZ5waahoxw3q2bZ myshell\]$
> \[user1@iZ5waahoxw3q2bZ myshell\]$
>
> 系统卡在这里是在等用户输入,我们想让自己的命令行也可以这样
如果是用scanf会把我们这个读成三部分
比如我们输入ls -a -l在shell看来时"ls" "-a" "-l"字符串。
[user1@iZ5waahoxw3q2bZ myshell]$ mv myshell.c myshell.cc
搞成C跟C++混编
[user1@iZ5waahoxw3q2bZ myshell]$ cat Makefile
myshell:myshell.cc
g++ -o $@ $^ #-std=c99
.PHONY:clean
clean:
rm -f myshell
fgets
char *fgets(char *s, int size, FILE *stream);
缓冲区大小
C标准可以从指定的文件流中获取信息,按行获取
成功了就是s的地址,失败就是NULL。FILE \*标准输入
为了避免将回车搞成多一行加入一行commandline\[strlen(commandline)-1\]=0;//清理\\n
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#define COMMAND_SIZE 1024
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
int main()
{
printf("[%s@%s %s]#",GetUserName(),GetHostName(),GetPwd());
char commandline[COMMAND_SIZE];
char *c = fgets(commandline,sizeof(commandline),stdin);
if(c==NULL) return 1;
commandline[strlen(commandline)-1]=0;//清理\n
printf("echo %s\n",commandline);
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ clear
[user1@iZ5waahoxw3q2bZ myshell]$ make
make: `myshell' is up to date.
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]#ls -a-^H ^C
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]#ls -a -l
echo ls -a -l
**int snprintf(char \*str, size_t size, const char \*format, ...);**
安全的格式化输出函数,用于将格式化字符串写入字符数组,并**限制最大写入长度**,防止缓冲区溢出。
> * **`snprintf`** 是 C 标准库函数,用于格式化字符串并写入缓冲区,同时保证不会超过缓冲区大小(防止溢出)。
>
> * **`cmd_prompt`**:输出缓冲区(字符数组)。
>
> * **`size`** :缓冲区总大小,`snprintf` 最多写入 `size-1` 个字符,末尾自动加 `'\0'`。
>
> * **`FORMAT`** :通常定义为类似 `"%s@%s:%s$ "` 的格式字符串,包含三个 `%s` 占位符。
>
> * **`GetUserName()`**:返回当前用户名(字符串)。
>
> * **`GetHostName()`**:返回主机名(字符串)。
>
> * **`GetPwd()`**:返回当前工作目录路径(字符串)。
#### 2.第二版
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
}
return 0;
}

#### 3.第三版
命令行参数在进行输出的时候,命令行参数这个表是由shell自己维护的
命令行分析 "ls -a -l" -\> "ls" "-a" "-l",我们可以用一个接口去分割
strtok 字符串切割
> SYNOPSIS
>
> #include \
>
> char \*strtok(char \*str, const char \*delim);
##### 命令行解析
[user1@iZ5waahoxw3q2bZ myshell]$ cat Makefile
myshell:myshell.cc
g++ -o $@ $^ -std=c++11 #-std=c99
.PHONY:clean
clean:
rm -f myshell
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
return true;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
CommandParse(commandline);
PrintArgv();
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ make
g++ -o myshell myshell.cc -std=c++11 #-std=c99
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# ls -a -l
echo ls -a -l
argv[0]->ls
argv[1]->-a
argv[2]->-l
argc:4
为什么这里是4呢?因为把NULL也算上了。加上一行g_argc--;
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return true;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
CommandParse(commandline);
PrintArgv();
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ make
g++ -o myshell myshell.cc -std=c++11 #-std=c99
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# ls -a -l
echo ls -a -l
argv[0]->ls
argv[1]->-a
argv[2]->-l
argc:3
已经按照对应的需求把命令行解析完了
这也符合曾经讲的shell提供两张表
##### 执行命令
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return true;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
CommandParse(commandline);
//PrintArgv();
//4.执行命令
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
//father
pid_t rid = waitpid(id,nullptr,0);
(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ make
g++ -o myshell myshell.cc -std=c++11 #-std=c99
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# ls -a -l
echo ls -a -l
total 32
drwxrwxr-x 2 user1 user1 4096 Apr 28 20:23 .
drwxrwxr-x 3 user1 user1 4096 Apr 28 17:28 ..
-rw-rw-r-- 1 user1 user1 90 Apr 28 20:02 Makefile
-rwxrwxr-x 1 user1 user1 13904 Apr 28 20:23 myshell
-rw-rw-r-- 1 user1 user1 2499 Apr 28 20:23 myshell.cc
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# pwd
echo pwd
/home/user1/linux-learning/linux/26-4-29/myshell
再改地优雅点
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return true;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
//father
pid_t rid = waitpid(id,nullptr,0);
(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
return 0;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
CommandParse(commandline);
//PrintArgv();
//4.执行命令
Execute();
}
return 0;
}
我们发现我们的命令都是带很长的路径的,修改一下
##### 小调整
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
// / /a/b/c
std::string DirName(const char *pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);//逆向找
if(pos == std::string::npos) return "BUG?";
return dir.substr(pos+1);//+1是为了不想加上前面/
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return true;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
//father
pid_t rid = waitpid(id,nullptr,0);
(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
return 0;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
CommandParse(commandline);
//PrintArgv();
//4.执行命令
Execute();
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ make
g++ -o myshell myshell.cc -std=c++11 #-std=c99
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ myshell]# ls -a -l
echo ls -a -l
total 32
drwxrwxr-x 2 user1 user1 4096 Apr 28 20:39 .
drwxrwxr-x 3 user1 user1 4096 Apr 28 17:28 ..
-rw-rw-r-- 1 user1 user1 90 Apr 28 20:02 Makefile
-rwxrwxr-x 1 user1 user1 14840 Apr 28 20:39 myshell
-rw-rw-r-- 1 user1 user1 2827 Apr 28 20:39 myshell.cc
[user1@iZ5waahoxw3q2bZ myshell]# pwd
echo pwd
#### 4.第四版
\[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell\]# cd ..
\[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell\]#
我们发现输入cd ..路径没有改变
为什么不变化呢?
**因为目前的命令都是让子进程去执行的!cd真正要更改的是bash的路径。所以遇到cd这样的命令不能让子进程去执行,应该让父进程自己亲自执行然后把自己切掉。这种命令称之为内建命令**
内建命令检查
> bool CheckAndExecBuiltin()
>
> {
>
> std::string cmd = g_argv\[0\];
>
> if(cmd == "cd")
>
> {
>
> return true;
>
> }
>
> return false;
>
> }
>
> //4.检测并执行内建命令
>
> if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
>
> continue;
>
CommandParse中
return g_argc \> 0 ? true : false;**向调用者返回命令解析是否成功**
if(!CommandParse(commandline)) continue;保证代码健壮性
##### chdir 更改工作路径 哪个进程调chdir就更改哪个路径
> **NAME**
>
> **chdir, fchdir - change working directory**
>
> **SYNOPSIS**
>
> **#include \**
>
> **int chdir(const char \*path);**
>
> **int fchdir(int fd);**
进程先变,然后就是环境变量
要执行内建命令要bash自己去执行
##### 内建命令
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
const char *pwd = getenv("PWD");
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()//获取家目录
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
// / /a/b/c
std::string DirName(const char *pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);//逆向找
if(pos == std::string::npos) return "BUG?";
return dir.substr(pos+1);//+1是为了不想加上前面/
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
//snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];//将命令解析后的第二个参数(g_argv[1])复制给一个 std::string 对象 where
//cd - /cd ~ //但是还有类似这两个这种不支持
if(where == "-")
{
//TODO
}
else if(where == "~")
{
//TODO
}
else
{
chdir(where.c_str());
}
}
return true;
}
return false;
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
//father
pid_t rid = waitpid(id,nullptr,0);
(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
return 0;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
//printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
//4.检测并执行内建命令
if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
continue;
//5.执行命令
Execute();
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# pwd
/home/user1/linux-learning/linux/26-4-29/myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# cd
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# pwd
/home/user1
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# cd /
[user1@iZ5waahoxw3q2bZ /home/user1/linux-learning/linux/26-4-29/myshell]# pwd
/
但是我们发现路径表示没有改变
所以其实当进程或者shell路径发生变化时,一定是进程路径先变才更新的环境变量。
所以不用环境变量去获取,用getcwd();
##### 系统调用
**getcwd 获得当前进程的工作路径**
> **NAME**
>
> **getcwd, getwd, get_current_dir_name - get current working directory**
>
> **SYNOPSIS**
>
> **#include \**
>
> **char \*getcwd(char \*buf, size_t size);**
>
> **char \*getwd(char \*buf);**
>
> **char \*get_current_dir_name(void);**
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
//for test
char cwd[1024];
char cwdenv[1024];
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
//const char *pwd = getenv("PWD");
const char *pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
putenv(cwdenv);
//return "None";
}
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()//获取家目录
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
//command
bool Cd()
{
///std::string where;
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];//将命令解析后的第二个参数(g_argv[1])复制给一个 std::string 对象 where
//cd - /cd ~ //但是还有类似这两个这种不支持
if(where == "-")
{
//TODO
}
else if(where == "~")
{
//TODO
}
else
{
chdir(where.c_str());
}
}
return true;
}
// / /a/b/c
std::string DirName(const char *pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);//逆向找
if(pos == std::string::npos) return "BUG?";
return dir.substr(pos+1);//+1是为了不想加上前面/
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
//snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
return false;
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
//father
pid_t rid = waitpid(id,nullptr,0);
(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
return 0;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
//printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
//4.检测并执行内建命令
if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
continue;
//5.执行命令
Execute();
}
return 0;
}
#### 5.echo
**int status = 0;//退出信息**
其实echo命令也是一条内建命令
linux中有些命令可能既属于内建又属于独立的命令,说明那些执行两份,shell执行一份,进程执行一份
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
//for test
char cwd[1024];
char cwdenv[1024];
//last exit code 最后退出码
int lastcode=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
//const char *pwd = getenv("PWD");
const char *pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
putenv(cwdenv);
//return "None";
}
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()//获取家目录
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
//command
bool Cd()
{
///std::string where;
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];//将命令解析后的第二个参数(g_argv[1])复制给一个 std::string 对象 where
//cd - /cd ~ //但是还有类似这两个这种不支持
if(where == "-")
{
//TODO
}
else if(where == "~")
{
//TODO
}
else
{
chdir(where.c_str());
}
}
return true;
}
// / /a/b/c
std::string DirName(const char *pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);//逆向找
if(pos == std::string::npos) return "BUG?";
return dir.substr(pos+1);//+1是为了不想加上前面/
}
void MakeCommandLine(char cmd_prompt[],int size)//制作一个commandline
{
snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
//snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt,sizeof(prompt));
printf("%s",prompt);
fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
//ls -a -l => "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
else if(cmd == "echo")
{
if(g_argc==2)//要有两个命令行参数
{
//echo "hello world"
//echo $?
//echo $PATH
std::string opt = g_argv[1];
if(opt == "$?")
{
std::cout<< lastcode <0)
{
lastcode=WEXITSTATUS(status);
}
return 0;
}
int main()
{
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
//printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
//4.检测并执行内建命令
if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
continue;
//5.执行命令
Execute();
}
return 0;
}
[user1@iZ5waahoxw3q2bZ myshell]$ make
g++ -o myshell myshell.cc -std=c++11 #-std=c99
[user1@iZ5waahoxw3q2bZ myshell]$ ./myshell
[user1@iZ5waahoxw3q2bZ myshell]# echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/user1/.local/bin:/home/user1/bin
[user1@iZ5waahoxw3q2bZ myshell]# echo $?
0
#### **shell有两张表,一张命令行参数表,一张环境变量表**
**environ** 是一个**指向环境变量数组的全局指针** ,在 C/C++(Unix/Linux)中可用。它定义在 `` 中(POSIX),允许程序直接访问整个环境变量列表。
> **SYNOPSIS
> #include \**
>
> **extern char \*\*environ;**
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
//环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs = 0;
//for test
char cwd[1024];
char cwdenv[1024];
//last exit code 最后退出码
int lastcode=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
//const char *pwd = getenv("PWD");
const char *pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
putenv(cwdenv);
//return "None";
}
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()//获取家目录
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
void InitEnv()//初始化一个用于保存环境变量的全局数据结构
{
extern char **environ;//声明一个环境变量所对应的信息
memset(g_env,0,sizeof(g_env));
g_envs=0;
//本来要从配置文件来
//1.获取环境变量
for(int i=0;environ[i];i++)
{
//申请空间
g_env[i]= (char*)malloc(strlen(environ[i])+1);
strcpy(g_env[i],environ[i]);
g_envs++;
}
g_env[g_envs++] = (char*)"HAHA=for_test";//for test 为了验证是我们自己的环境变量,如果有就说明是
g_env[g_envs] = NULL;
//2.导成环境变量
for(int i =0; g_env[i];i++)
{
putenv(g_env[i]);
}
}
//command
bool Cd()
{
///std::string where;
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];//将命令解析后的第二个参数(g_argv[1])复制给一个 std::string 对象 where
//cd - /cd ~ //但是还有类似这两个这种不支持
if(where == "-")
{
//TODO
}
else if(where == "~")
{
//TODO
}
else
{
chdir(where.c_str());
}
}
return true;
}
void Echo()
{
if(g_argc==2)//要有两个命令行参数
{
//echo "hello world"
//echo $?
//echo $PATH
std::string opt = g_argv[1];
if(opt == "$?")
{
std::cout<< lastcode < "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
else if(cmd == "echo")
{
Echo();
return true;
}
return false;
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
int status = 0;//退出信息
//father
pid_t rid = waitpid(id,&status,0);
//(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
if(rid>0)
{
lastcode=WEXITSTATUS(status);
}
return 0;
}
int main()
{
// shell启动的时候,需要从系统中获取环境变量
// 我们的环境变量信息应该从父shell统一来
InitEnv();
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
//printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
//4.检测并执行内建命令
if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
continue;
//5.执行命令
Execute();
}
//cleanup();
return 0;
}
然后make,接着./myshell之后env,发现我们环境变量表里面有这个HAHA=for_test
所以这样也就能明白之前讲的export
#### export
> **`export` 的核心作用**
>
> * 将一个变量标记为 **环境变量**,添加到当前 Shell 的环境表中。
>
> * 之后通过 `fork` 创建的子进程(例如执行外部命令)会**自动复制**当前的环境变量,从而能访问到这些导出的变量。
#### **alias也是一个内建命令**
[user1@iZ5waahoxw3q2bZ myshell]$ cat myshell.cc
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
//1.命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
//2.环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs = 0;
//3.别名映射表
std::unordered_map alias_list;
//for test
char cwd[1024];
char cwdenv[1024];
//last exit code 最后退出码
int lastcode=0;
const char *GetUserName()//获取用户名
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()//获取主机名
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()//获取当前路径
{
//const char *pwd = getenv("PWD");
const char *pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
putenv(cwdenv);
//return "None";
}
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()//获取家目录
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
void InitEnv()//初始化一个用于保存环境变量的全局数据结构
{
extern char **environ;//声明一个环境变量所对应的信息
memset(g_env,0,sizeof(g_env));
g_envs=0;
//本来要从配置文件来
//1.获取环境变量
for(int i=0;environ[i];i++)
{
//申请空间
g_env[i]= (char*)malloc(strlen(environ[i])+1);
strcpy(g_env[i],environ[i]);
g_envs++;
}
g_env[g_envs++] = (char*)"HAHA=for_test";//for test 为了验证是我们自己的环境变量,如果有就说明是
g_env[g_envs] = NULL;
//2.导成环境变量
for(int i =0; g_env[i];i++)
{
putenv(g_env[i]);
}
environ = g_env;
}
//command
bool Cd()
{
///std::string where;
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];//将命令解析后的第二个参数(g_argv[1])复制给一个 std::string 对象 where
//cd - /cd ~ //但是还有类似这两个这种不支持
if(where == "-")
{
//TODO
}
else if(where == "~")
{
//TODO
}
else
{
chdir(where.c_str());
}
}
return true;
}
void Echo()
{
if(g_argc==2)//要有两个命令行参数
{
//echo "hello world"
//echo $?
//echo $PATH
std::string opt = g_argv[1];
if(opt == "$?")
{
std::cout<< lastcode < "ls -a -l\n" 字符串
char *c = fgets(out,size,stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0;//清理\n
if(strlen(out) == 0) return false;
return true;
}
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc=0;
//命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline,SEP);
while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i=0;g_argv[i];i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc:%d\n",g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
else if(cmd == "echo")
{
Echo();
return true;
}
else if(cmd == "export")
{
}
else if(cmd == "alias")
{
//std::string nickname = g_argv[i];
//alias_list.insert(k,v);
}
return false;
}
int Execute()//执行命令
{
pid_t id = fork();
if(id==0)
{
//child
//程序替换
execvp(g_argv[0],g_argv);
exit(1);
}
int status = 0;//退出信息
//father
pid_t rid = waitpid(id,&status,0);
//(void)rid; //让rid使用一下,为了消除"未使用的变量 rid"的编译器警告
if(rid>0)
{
lastcode=WEXITSTATUS(status);
}
return 0;
}
int main()
{
// shell启动的时候,需要从系统中获取环境变量
// 我们的环境变量信息应该从父shell统一来
InitEnv();
while(true)
{
//1.输出命令行提示符
PrintCommandPrompt();
//2.获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline,sizeof(commandline)))
continue;
//printf("echo %s\n",commandline);
//3.命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
//检测别名
//4.检测并执行内建命令
if(CheckAndExecBuiltin())//是内建命令就不再向下执行代码,避免创建子进程去执行
continue;
//5.执行命令
Execute();
}
//cleanup();
return 0;
}
| 变量 | 含义 | 示例(命令 `ls -a -l`) |
|----------|-------------|-----------------------------------------------------------------------------|
| `g_argc` | 命令中单词的个数 | `3`("ls"、"-a"、"-l") |
| `g_argv` | 指向每个单词的指针数组 | `g_argv[0] → "ls"` `g_argv[1] → "-a"` `g_argv[2] → "-l"` `g_argv[3] → NULL` |
**真正执行系统命令的地方是 `Execute()` 函数内部的 `execvp` 调用**。
感谢你的观看,期待我们下次再见!