进程控制(下)

文章目录

    • [5. 自主Shell命令行解释器](#5. 自主Shell命令行解释器)

5. 自主Shell命令行解释器

c 复制代码
[gsm@VM-4-3-centos myshell]$ touch myshell.cc
[gsm@VM-4-3-centos myshell]$ ll
total 0
-rw-rw-r-- 1 gsm gsm 0 Nov 26 14:00 myshell.cc
[gsm@VM-4-3-centos myshell]$ ls > Makefile
[gsm@VM-4-3-centos myshell]$ vim Makefile 
[gsm@VM-4-3-centos myshell]$ cat Makefile 
CC:=g++
myshell:myshell.cc
	$(CC) -o $@ $^
.PHONY:clean
clean:
	rm -f myshell
[gsm@VM-4-3-centos myshell]$ vim Makefile 
[gsm@VM-4-3-centos myshell]$ cat Makefile 
CC:=g++
myshell:myshell.cc
	$(CC) -o $@ $^
.PHONY:clean
clean:
	rm -f myshell
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc
[gsm@VM-4-3-centos myshell]$ ll
total 20
-rw-rw-r-- 1 gsm gsm   78 Nov 26 14:08 Makefile
-rwxrwxr-x 1 gsm gsm 8656 Nov 26 14:08 myshell
-rw-rw-r-- 1 gsm gsm   91 Nov 26 14:07 myshell.cc
[gsm@VM-4-3-centos myshell]$ make clean
rm -f myshell
[gsm@VM-4-3-centos myshell]$ ll
total 8
-rw-rw-r-- 1 gsm gsm 78 Nov 26 14:08 Makefile
-rw-rw-r-- 1 gsm gsm 91 Nov 26 14:07 myshell.cc
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ 
[gsm@VM-4-3-centos myshell]$ clear
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

const int basesize = 1024;

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    string pwd = getenv("PWD");
    return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}

int main()
{
    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符
        printf("\n");
        sleep(1);

        //GetCommandLine();   // 2. 获取用户命令

        //ParseCommandLine(); // 3. 分析命令

        //ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
^C

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 8
-rw-rw-r-- 1 gsm gsm   78 Nov 26 14:08 Makefile
-rw-rw-r-- 1 gsm gsm 1584 Nov 26 15:34 myshell.cc
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

const int basesize = 1024;

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    string pwd = getenv("PWD");
    return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;

    return true;
}

int main()
{
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }

        printf("%s\n", command_buffer);

        //ParseCommandLine(); // 3. 分析命令

        //ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls
ls
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# pwd
pwd
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -a -l
ls -a -l
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -a -b -c -d
ls -a -b -c -d

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 8
-rw-rw-r-- 1 gsm gsm   78 Nov 26 14:08 Makefile
-rw-rw-r-- 1 gsm gsm 1786 Nov 26 15:54 myshell.cc
[gsm@VM-4-3-centos myshell]$ vim Makefile 
[gsm@VM-4-3-centos myshell]$ cat Makefile 
CC:=g++
myshell:myshell.cc
	$(CC) -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f myshell
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    string pwd = getenv("PWD");
    return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

void debug()
{
    printf("argc: %d\n", gargc);

    for (int i = 0; gargv[i]; i++)
    {
        printf("argv[%d]: %s\n", i, gargv[i]);
    }
}

int main()
{
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        debug();

        //ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls
argc: 1
argv[0]: ls
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -a -l
argc: 3
argv[0]: ls
argv[1]: -a
argv[2]: -l
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls   -a    --l
argc: 3
argv[0]: ls
argv[1]: -a
argv[2]: --l
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -a -l -a -c -d -ef -w
argc: 8
argv[0]: ls
argv[1]: -a
argv[2]: -l
argv[3]: -a
argv[4]: -c
argv[5]: -d
argv[6]: -ef
argv[7]: -w

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 8
-rw-rw-r-- 1 gsm gsm   89 Nov 26 17:16 Makefile
-rw-rw-r-- 1 gsm gsm 2509 Nov 26 18:35 myshell.cc
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    string pwd = getenv("PWD");
    return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvp(gargv[0], gargv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        // Do Nothing
        return true;
    }

    return false;
}

int main()
{
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls
Makefile  myshell  myshell.cc
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# pwd
/home/gsm/linux/112/lesson17/myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# top
top - 22:38:45 up 52 days,  7:22,  1 user,  load average: 0.05, 0.33, 0.22
Tasks:  95 total,   1 running,  94 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.5 us,  0.5 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2046500 total,   354892 free,   279176 used,  1412432 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1569908 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                      
25273 root      20   0 1039700 121624  16576 S   1.0  5.9 536:07.77 YDService                                                                                                                                    
 1606 root      20   0  821564  17920   2844 S   0.7  0.9 696:26.99 barad_agent                                                                                                                                  
    1 root      20   0   43592   3640   2320 S   0.0  0.2   4:28.78 systemd                                                                                                                                      
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.79 kthreadd                                                                                                                                     
    4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H                                                                                                                                 
    6 root      20   0       0      0      0 S   0.0  0.0   0:42.62 ksoftirqd/0                                                                                                                                  
    7 root      rt   0       0      0      0 S   0.0  0.0   0:19.78 migration/0                                                                                                                                  
    8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh                                                                                                                                       
    9 root      20   0       0      0      0 S   0.0  0.0   9:47.79 rcu_sched                                                                                                                                    
   10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 lru-add-drain                                                                                                                                
   11 root      rt   0       0      0      0 S   0.0  0.0   0:10.57 watchdog/0                                                                                                                                   
   12 root      rt   0       0      0      0 S   0.0  0.0   0:08.56 watchdog/1                                                                                                                                   
   13 root      rt   0       0      0      0 S   0.0  0.0   0:20.04 migration/1                                                                                                                                  
   14 root      20   0       0      0      0 S   0.0  0.0   0:40.55 ksoftirqd/1                                                                                                                                  
   16 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/1:0H                                                                                                                                 
   18 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kdevtmpfs                                                                                                                                    
   19 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 netns                                                                                                                                        
   20 root      20   0       0      0      0 S   0.0  0.0   0:00.86 khungtaskd                                                                                                                                   
   21 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 writeback                                                                                                                                    
   22 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kintegrityd                                                                                                                                  
   23 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                                                                                                                                       
   24 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                                                                                                                                       
   25 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                                                                                                                                       
   26 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kblockd                                                                                                                                      
   27 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 md                                                                                                                                           
   28 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 edac-poller                                                                                                                                  
   29 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 watchdogd                                                                                                                                    
   35 root      20   0       0      0      0 S   0.0  0.0   0:01.05 kswapd0                                                                                                                                      
   36 root      25   5       0      0      0 S   0.0  0.0   0:00.00 ksmd                                                                                                                                         
   37 root      39  19       0      0      0 S   0.0  0.0   0:04.38 khugepaged                                                                                                                                   
   38 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 crypto                                                                                                                                       
   46 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kthrotld                                                                                                                                     
   48 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kmpath_rdacd                                                                                                                                 
   49 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kaluad                                                                                                                                       
   50 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kpsmoused                                                                                                                                    
   51 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 ipv6_addrconf                                                                                                                                
   65 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 deferwq                                                                                                                                      
  108 root      20   0       0      0      0 S   0.0  0.0   0:12.60 kauditd                                                                                                                                      
  200 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 iscsi_eh                                                                                                                                     
  259 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 ata_sff                                                                                                                                      
  262 root      20   0       0      0      0 S   0.0  0.0   0:00.00 scsi_eh_0                                                                                                                                    
  263 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 scsi_tmf_0                                                                                                                                   
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -l
total 24
-rw-rw-r-- 1 gsm gsm    89 Nov 26 17:16 Makefile
-rwxrwxr-x 1 gsm gsm 14664 Nov 26 22:36 myshell
-rw-rw-r-- 1 gsm gsm  2824 Nov 26 22:36 myshell.cc
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -l -a
total 32
drwxrwxr-x 2 gsm gsm  4096 Nov 26 22:36 .
drwxrwxr-x 3 gsm gsm  4096 Nov 26 13:59 ..
-rw-rw-r-- 1 gsm gsm    89 Nov 26 17:16 Makefile
-rwxrwxr-x 1 gsm gsm 14664 Nov 26 22:36 myshell
-rw-rw-r-- 1 gsm gsm  2824 Nov 26 22:36 myshell.cc
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# touch temp.txt
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -l -a
total 32
drwxrwxr-x 2 gsm gsm  4096 Nov 26 22:39 .
drwxrwxr-x 3 gsm gsm  4096 Nov 26 13:59 ..
-rw-rw-r-- 1 gsm gsm    89 Nov 26 17:16 Makefile
-rwxrwxr-x 1 gsm gsm 14664 Nov 26 22:36 myshell
-rw-rw-r-- 1 gsm gsm  2824 Nov 26 22:36 myshell.cc
-rw-rw-r-- 1 gsm gsm     0 Nov 26 22:39 temp.txt
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# rm temp.txt
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls -a -l
total 32
drwxrwxr-x 2 gsm gsm  4096 Nov 26 22:40 .
drwxrwxr-x 3 gsm gsm  4096 Nov 26 13:59 ..
-rw-rw-r-- 1 gsm gsm    89 Nov 26 17:16 Makefile
-rwxrwxr-x 1 gsm gsm 14664 Nov 26 22:36 myshell
-rw-rw-r-- 1 gsm gsm  2824 Nov 26 22:36 myshell.cc

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 8
-rw-rw-r-- 1 gsm gsm   89 Nov 26 17:16 Makefile
-rw-rw-r-- 1 gsm gsm 3522 Nov 27 15:41 myshell.cc
[gsm@VM-4-3-centos myshell]$ man getcwd
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    if (nullptr == getcwd(pwd, sizeof(pwd)))
    {
        return "None";
    }
    
    snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
    putenv(pwdenv); // PWD=XXX

    return pwd;
    
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvp(gargv[0], gargv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        // Do Nothing
        return true;
    }

    return false;
}

// shell自己执行命令,本质是shell调用自己的函数
bool CheckiAndExecBuiltCommand()
{
    if (strcmp(gargv[0], "cd") == 0)
    {
        // 内建命令
        if (gargc == 2)
        {
            chdir(gargv[1]);
        }

        return true;
    }

    return false;
}

int main()
{
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        if (CheckiAndExecBuiltCommand())
        {
            continue;
        }

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# pwd
/home/gsm/linux/112/lesson17/myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# cd ..
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17]# pwd
/home/gsm/linux/112/lesson17
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17]# cd /
[gsm@VM-4-3-centos /]# pwd
/
[gsm@VM-4-3-centos /]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 12
-rw-rw-r-- 1 gsm gsm   89 Nov 26 17:16 Makefile
-rw-rw-r-- 1 gsm gsm 4331 Nov 27 16:55 myshell.cc
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

// 我的系统的环境变量
char* genv[envnum];

// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    if (nullptr == getcwd(pwd, sizeof(pwd)))
    {
        return "None";
    }
    
    snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
    putenv(pwdenv); // PWD=XXX

    return pwd;
    
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvp(gargv[0], gargv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        // Do Nothing
        return true;
    }

    return false;
}

void AddEnv(const char* item)
{
    int index = 0;

    while (genv[index])
    {
        index++;
    }

    genv[index] = (char*)malloc(strlen(item) + 1);
    strncpy(genv[index], item, strlen(item) + 1);
    genv[++index] = nullptr;
}

// shell自己执行命令,本质是shell调用自己的函数
bool CheckiAndExecBuiltCommand()
{
    if (strcmp(gargv[0], "cd") == 0)
    {
        // 内建命令
        if (gargc == 2)
        {
            chdir(gargv[1]);
        }

        return true;
    }
    else if(strcmp(gargv[0], "export") == 0)
    {
        // export也是内建命令
        if (gargc == 2)
        {
            AddEnv(gargv[1]);
        }

        return true;
    }
    else if (strcmp(gargv[0], "env") == 0)
    {
        for (int i = 0; genv[i]; i++)
        {
            printf("%s\n", genv[i]);
        }

        return true;
    }

    return false;
}

// 作为一个shell,获取环境变量应该从系统的配置来
// 我们今天就直接从父shell中获取环境变量
void InitEnv()
{
    extern char** environ;
    int index = 0;

    while (environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]) + 1);
        strncpy(genv[index], environ[index], strlen(environ[index]) + 1);
        index++;
    }

    genv[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        if (CheckiAndExecBuiltCommand())
        {
            continue;
        }

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# export HAHA=AAAAAAAAAA
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
HAHA=AAAAAAAAAA
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# export hehe=BBBBBBBBBBBBBBB
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
HAHA=AAAAAAAAAA
hehe=BBBBBBBBBBBBBBB

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 12
-rw-rw-r-- 1 gsm gsm   89 Nov 26 17:16 Makefile
-rw-rw-r-- 1 gsm gsm 4740 Nov 27 18:20 myshell.cc
[gsm@VM-4-3-centos myshell]$ touch testenv.c
[gsm@VM-4-3-centos myshell]$ vim testenv.c 
[gsm@VM-4-3-centos myshell]$ cat testenv.c 
#include <stdio.h>

int main(int argc, char* argv[], char* env[])
{
    for (int i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n", i, argv[i]);
    }

    for (int i = 0; env[i]; i++)
    {
        printf("env[%d]: %s\n", i, env[i]);
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ gcc -o testenv testenv.c -std=c99
[gsm@VM-4-3-centos myshell]$ ./testenv 
argv[0]: ./testenv
env[0]: XDG_SESSION_ID=95407
env[1]: HOSTNAME=VM-4-3-centos
env[2]: TERM=xterm
env[3]: SHELL=/bin/bash
env[4]: HISTSIZE=3000
env[5]: SSH_CLIENT=101.94.69.10 61355 22
env[6]: SSH_TTY=/dev/pts/0
env[7]: USER=gsm
env[8]: LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
env[9]: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
env[10]: MAIL=/var/spool/mail/gsm
env[11]: PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
env[12]: PWD=/home/gsm/linux/112/lesson17/myshell
env[13]: LANG=en_US.utf8
env[14]: SHLVL=1
env[15]: HOME=/home/gsm
env[16]: LOGNAME=gsm
env[17]: SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
env[18]: LESSOPEN=||/usr/bin/lesspipe.sh %s
env[19]: PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
env[20]: XDG_RUNTIME_DIR=/run/user/1001
env[21]: HISTTIMEFORMAT=%F %T 
env[22]: OLDPWD=/home/gsm/linux/112/lesson17
env[23]: _=./testenv
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

// 我的系统的环境变量
char* genv[envnum];

// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    if (nullptr == getcwd(pwd, sizeof(pwd)))
    {
        return "None";
    }
    
    snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
    putenv(pwdenv); // PWD=XXX

    return pwd;
    
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvpe(gargv[0], gargv, genv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        // Do Nothing
        return true;
    }

    return false;
}

void AddEnv(const char* item)
{
    int index = 0;

    while (genv[index])
    {
        index++;
    }

    genv[index] = (char*)malloc(strlen(item) + 1);
    strncpy(genv[index], item, strlen(item) + 1);
    genv[++index] = nullptr;
}

// shell自己执行命令,本质是shell调用自己的函数
bool CheckiAndExecBuiltCommand()
{
    if (strcmp(gargv[0], "cd") == 0)
    {
        // 内建命令
        if (gargc == 2)
        {
            chdir(gargv[1]);
        }

        return true;
    }
    else if(strcmp(gargv[0], "export") == 0)
    {
        // export也是内建命令
        if (gargc == 2)
        {
            AddEnv(gargv[1]);
        }

        return true;
    }
    else if (strcmp(gargv[0], "env") == 0)
    {
        for (int i = 0; genv[i]; i++)
        {
            printf("%s\n", genv[i]);
        }

        return true;
    }

    return false;
}

// 作为一个shell,获取环境变量应该从系统的配置来
// 我们今天就直接从父shell中获取环境变量
void InitEnv()
{
    extern char** environ;
    int index = 0;

    while (environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]) + 1);
        strncpy(genv[index], environ[index], strlen(environ[index]) + 1);
        index++;
    }

    genv[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        if (CheckiAndExecBuiltCommand())
        {
            continue;
        }

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ./testenv
argv[0]: ./testenv
env[0]: XDG_SESSION_ID=95407
env[1]: HOSTNAME=VM-4-3-centos
env[2]: TERM=xterm
env[3]: SHELL=/bin/bash
env[4]: HISTSIZE=3000
env[5]: SSH_CLIENT=101.94.69.10 61355 22
env[6]: SSH_TTY=/dev/pts/0
env[7]: USER=gsm
env[8]: LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
env[9]: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
env[10]: MAIL=/var/spool/mail/gsm
env[11]: PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
env[12]: PWD=/home/gsm/linux/112/lesson17/myshell
env[13]: LANG=en_US.utf8
env[14]: SHLVL=1
env[15]: HOME=/home/gsm
env[16]: LOGNAME=gsm
env[17]: SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
env[18]: LESSOPEN=||/usr/bin/lesspipe.sh %s
env[19]: PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
env[20]: XDG_RUNTIME_DIR=/run/user/1001
env[21]: HISTTIMEFORMAT=%F %T 
env[22]: OLDPWD=/home/gsm/linux/112/lesson17
env[23]: _=./myshell
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# export HAHA=aaaaaaaaaaaaaaaa
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=./myshell
HAHA=aaaaaaaaaaaaaaaa
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ./testenv
argv[0]: ./testenv
env[0]: XDG_SESSION_ID=95407
env[1]: HOSTNAME=VM-4-3-centos
env[2]: TERM=xterm
env[3]: SHELL=/bin/bash
env[4]: HISTSIZE=3000
env[5]: SSH_CLIENT=101.94.69.10 61355 22
env[6]: SSH_TTY=/dev/pts/0
env[7]: USER=gsm
env[8]: LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
env[9]: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
env[10]: MAIL=/var/spool/mail/gsm
env[11]: PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
env[12]: PWD=/home/gsm/linux/112/lesson17/myshell
env[13]: LANG=en_US.utf8
env[14]: SHLVL=1
env[15]: HOME=/home/gsm
env[16]: LOGNAME=gsm
env[17]: SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
env[18]: LESSOPEN=||/usr/bin/lesspipe.sh %s
env[19]: PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
env[20]: XDG_RUNTIME_DIR=/run/user/1001
env[21]: HISTTIMEFORMAT=%F %T 
env[22]: OLDPWD=/home/gsm/linux/112/lesson17
env[23]: _=./myshell
env[24]: HAHA=aaaaaaaaaaaaaaaa

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 48
-rw-rw-r-- 1 gsm gsm    89 Nov 26 17:16 Makefile
-rwxrwxr-x 1 gsm gsm 19448 Nov 27 18:55 myshell
-rw-rw-r-- 1 gsm gsm  4747 Nov 27 18:55 myshell.cc
-rwxrwxr-x 1 gsm gsm  8360 Nov 27 18:45 testenv
-rw-rw-r-- 1 gsm gsm   268 Nov 27 18:45 testenv.c
[gsm@VM-4-3-centos myshell]$ a=100
[gsm@VM-4-3-centos myshell]$ echo $a
100
[gsm@VM-4-3-centos myshell]$ env
XDG_SESSION_ID=95407
HOSTNAME=VM-4-3-centos
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=101.94.69.10 61355 22
SSH_TTY=/dev/pts/0
USER=gsm
LD_LIBRARY_PATH=:/home/gsm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/gsm
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/gsm/.local/bin:/home/gsm/bin
PWD=/home/gsm/linux/112/lesson17/myshell
LANG=en_US.utf8
SHLVL=1
HOME=/home/gsm
LOGNAME=gsm
SSH_CONNECTION=101.94.69.10 61355 10.0.4.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1001
HISTTIMEFORMAT=%F %T 
OLDPWD=/home/gsm/linux/112/lesson17
_=/usr/bin/env
[gsm@VM-4-3-centos myshell]$ ls adfafag
ls: cannot access adfafag: No such file or directory
[gsm@VM-4-3-centos myshell]$ echo $?
2
[gsm@VM-4-3-centos myshell]$ echo $?
0
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

// 全局的变量
int lastcode = 0;

// 我的系统的环境变量
char* genv[envnum];

// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    if (nullptr == getcwd(pwd, sizeof(pwd)))
    {
        return "None";
    }
    
    snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
    putenv(pwdenv); // PWD=XXX

    return pwd;
    
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None": pwd;
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvpe(gargv[0], gargv, genv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        if (WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else
        {
            lastcode = 100;
        }

        return true;
    }

    return false;
}

void AddEnv(const char* item)
{
    int index = 0;

    while (genv[index])
    {
        index++;
    }

    genv[index] = (char*)malloc(strlen(item) + 1);
    strncpy(genv[index], item, strlen(item) + 1);
    genv[++index] = nullptr;
}

// shell自己执行命令,本质是shell调用自己的函数
bool CheckiAndExecBuiltCommand()
{
    if (strcmp(gargv[0], "cd") == 0)
    {
        // 内建命令
        if (gargc == 2)
        {
            chdir(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 1;
        }

        return true;
    }
    else if(strcmp(gargv[0], "export") == 0)
    {
        // export也是内建命令
        if (gargc == 2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 2;
        }

        return true;
    }
    else if (strcmp(gargv[0], "env") == 0)
    {
        for (int i = 0; genv[i]; i++)
        {
            printf("%s\n", genv[i]);
        }

        lastcode = 0;

        return true;
    }
    else if (strcmp(gargv[0], "echo") == 0)
    {
        if (gargc == 2)
        {
            // echo $?
            // echo $PATH
            // echo hello
            if (gargv[1][0] == '$')
            {
                if (gargv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
            }
            else
            {
                printf("%s\n", gargv[1]);
                lastcode = 0;
            }
        }
        else
        {
            lastcode = 3;
        }

        return true;
    }

    return false;
}

// 作为一个shell,获取环境变量应该从系统的配置来
// 我们今天就直接从父shell中获取环境变量
void InitEnv()
{
    extern char** environ;
    int index = 0;

    while (environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]) + 1);
        strncpy(genv[index], environ[index], strlen(environ[index]) + 1);
        index++;
    }

    genv[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        if (CheckiAndExecBuiltCommand())
        {
            continue;
        }

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls
Makefile  myshell  myshell.cc  testenv	testenv.c
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
0
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# ls asdfsfs
ls: cannot access asdfsfs: No such file or directory
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
2
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
0
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo hello
hello
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
0
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# export
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
2
[gsm@VM-4-3-centos /home/gsm/linux/112/lesson17/myshell]# echo $?
0

c 复制代码
[gsm@VM-4-3-centos myshell]$ ll
total 28
-rw-rw-r-- 1 gsm gsm   89 Nov 26 17:16 Makefile
-rw-rw-r-- 1 gsm gsm 5706 Nov 28 22:26 myshell.cc
-rwxrwxr-x 1 gsm gsm 8360 Nov 27 18:45 testenv
-rw-rw-r-- 1 gsm gsm  268 Nov 27 18:45 testenv.c
[gsm@VM-4-3-centos myshell]$ vim myshell.cc 
[gsm@VM-4-3-centos myshell]$ cat myshell.cc 
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

// 全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

// 全局的变量
int lastcode = 0;

// 我的系统的环境变量
char* genv[envnum];

// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    if (nullptr == getcwd(pwd, sizeof(pwd)))
    {
        return "None";
    }
    
    snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
    putenv(pwdenv); // PWD=XXX

    return pwd;
    
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None": pwd;
}

string LastDir()
{
    string curr = GetPwd();

    if (curr == "/" || curr == "None")
    {
        return curr;
    }

    // /home/gsm/XXX
    size_t pos = curr.rfind("/");

    if (pos == std::string::npos)
    {
        return curr;
    }

    return curr.substr(pos + 1);
}

string MakeCommandLine()
{
    // [gsm@VM-4-3-centos myshell]$
    char command_line[basesize];
    snprintf(command_line, basesize, "[%s@%s %s]# ",\
            GetUserName().c_str(), GetHostName().c_str(), LastDir().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}
bool GetCommandLine(char command_buffer[], int size)
{
    // 我们认为:我们要将用户输入的命令行,当作一个完整的字符串
    // "ls -a -l -n"
    char* result = fgets(command_buffer, size, stdin);

    if (!result)
    {
        return false;
    }

    // 输入abcde5个字符
    // 数组中实际存放了6个字符,因为还有一个回车键
    // 我们需要一个纯净的字符串,不想要这个'\n'
    command_buffer[strlen(command_buffer) - 1] = 0;
    
    if (strlen(command_buffer) == 0)
    {
        return false;
    }

    return true;
}
void ParseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);

    // =是刻意写的
    while ((bool)(gargv[gargc++] = strtok(nullptr, sep)));

    gargc--;
}

// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
    // 让子进程进行执行
    pid_t id = fork();

    if (id < 0)
    {
        return false;
    }

    if (id == 0)
    {
        // 子进程
        // 1. 执行命令
        execvpe(gargv[0], gargv, genv);
        // 2. 退出
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);

    if (rid > 0)
    {
        if (WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else
        {
            lastcode = 100;
        }

        return true;
    }

    return false;
}

void AddEnv(const char* item)
{
    int index = 0;

    while (genv[index])
    {
        index++;
    }

    genv[index] = (char*)malloc(strlen(item) + 1);
    strncpy(genv[index], item, strlen(item) + 1);
    genv[++index] = nullptr;
}

// shell自己执行命令,本质是shell调用自己的函数
bool CheckiAndExecBuiltCommand()
{
    if (strcmp(gargv[0], "cd") == 0)
    {
        // 内建命令
        if (gargc == 2)
        {
            chdir(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 1;
        }

        return true;
    }
    else if(strcmp(gargv[0], "export") == 0)
    {
        // export也是内建命令
        if (gargc == 2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 2;
        }

        return true;
    }
    else if (strcmp(gargv[0], "env") == 0)
    {
        for (int i = 0; genv[i]; i++)
        {
            printf("%s\n", genv[i]);
        }

        lastcode = 0;

        return true;
    }
    else if (strcmp(gargv[0], "echo") == 0)
    {
        if (gargc == 2)
        {
            // echo $?
            // echo $PATH
            // echo hello
            if (gargv[1][0] == '$')
            {
                if (gargv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
            }
            else
            {
                printf("%s\n", gargv[1]);
                lastcode = 0;
            }
        }
        else
        {
            lastcode = 3;
        }

        return true;
    }

    return false;
}

// 作为一个shell,获取环境变量应该从系统的配置来
// 我们今天就直接从父shell中获取环境变量
void InitEnv()
{
    extern char** environ;
    int index = 0;

    while (environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]) + 1);
        strncpy(genv[index], environ[index], strlen(environ[index]) + 1);
        index++;
    }

    genv[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];

    while (true)
    {
        PrintCommandLine(); // 1. 命令行提示符

        // command_buffer -> output
        if (!GetCommandLine(command_buffer, basesize)) // 2. 获取用户命令
        {
            continue;
        }
        
        //ls
        //"ls -a -b -c -d"->"ls" "-a" "-b" "-c" "-d"
        ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令

        if (CheckiAndExecBuiltCommand())
        {
            continue;
        }

        ExecuteCommand();   // 4. 执行命令
    }

    return 0;
}
[gsm@VM-4-3-centos myshell]$ make
g++ -o myshell myshell.cc -std=c++11
[gsm@VM-4-3-centos myshell]$ ./myshell 
[gsm@VM-4-3-centos myshell]# cd /
[gsm@VM-4-3-centos /]# pwd
/
[gsm@VM-4-3-centos /]# cd /home/gsm
[gsm@VM-4-3-centos gsm]# pwd
/home/gsm
[gsm@VM-4-3-centos gsm]# cd linux
[gsm@VM-4-3-centos linux]# pwd
/home/gsm/linux
[gsm@VM-4-3-centos linux]# cd 112
[gsm@VM-4-3-centos 112]# pwd
/home/gsm/linux/112
[gsm@VM-4-3-centos 112]# cd /
[gsm@VM-4-3-centos /]# pwd
/
相关推荐
xiaobuding_QAQ2 小时前
51汇编仿真proteus8.15学习篇二(附源码)
开发语言·汇编·单片机·学习·proteus
兵哥工控2 小时前
mfc两个线程的创建、启动、安全结束实例
c++·mfc·多线程·线程安全退出
Miketutu2 小时前
Dart基础学习
开发语言·windows
小龙报2 小时前
【算法通关指南:算法基础篇 】双指针专题:1.唯一的雪花 2.逛画展 3.字符串 4.丢手绢
c语言·数据结构·c++·人工智能·深度学习·算法·信息与通信
ai_xiaogui2 小时前
Debian系统PVE虚拟机安装详解:ISO镜像上传+硬件配置+图形化安装指南
运维·debian·php·panelai兼容测试·图形化安装指南·iso镜像上传配置·debian pve虚拟机安装
bybitq2 小时前
Go中的闭包函数Closure
开发语言·后端·golang
FirstFrost --sy2 小时前
Qt控件美化:LineEdit与CheckBox实战
开发语言·qt
Yusei_05232 小时前
Redis核心特性与应用全解析
开发语言·数据库·c++·redis·缓存
周周记笔记2 小时前
Ubuntu 中如何配置域名
运维·服务器