【Linux】自定义shell(讲解原理)

目录

一、打印命令提示符和获取命令行命令字符串

1.1 设计

我们首先使用操作系统的bash看到了命令行提示符的组成为[用户名@主机名 当前工作目录]$,获取用户名、主机名和当前工作目录的函数在系统调用中都有,这里我们自己设计一个,这三个数据都是环境变量,我们可以通过getenv来获取到他们,获取后将他们按照操作系统bash的格式输出出来即可,通过下图我们可以发现,我们的当前工作目录与操作系统的有所区别,我们的当前工作目录是一条路径,可以通过裁剪得到与操作系统一样的效果,我这里为了区分与操作系统的区别,这里就不做裁剪了。

输出完命令行提示符后,就需要向bash中输入命令了,这里我们就需要一个输入函数来读取命令字符串,需要注意的是这里不能使用scanf函数,因为scanf函数不能读取空格之后的内容,可以选择gets/fgets函数来读取,当我们输入完命令字符串后需要按回车,那么获取到的字符串中也会获取到这个'\n',所以我们还需要将这个'\n'处理掉。

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define NUM 1024

const char* getUsername()
{
    char* username = getenv("USER");
    if(username)
        return username;
    else
        return "none"; 
}

const char* getHostname()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname)
        return hostname;
    else
        return "none"; 
}


const char* getCwd()
{
    char* cwd = getenv("PWD");                                                                                                               
    if(cwd)                                                                                                                                  
        return cwd;                                                                                                                                      
    else                                                                                                                                     
        return "none";     
}

int main()  
{
    char usercommand[NUM]; 
    printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
    
    // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf
    fgets(usercommand,sizeof(usercommand),stdin);
    // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
    usercommand[strlen(usercommand)-1] = '\0';

	// 用于测试输入的字符串是否符合我们的预期
    printf("%s",usercommand);

    return 0;
}

1.2 封装

这里将打印命令行提示符与获取命令行字符串的工作统一封装到getUserCommand这个函数中。

cpp 复制代码
int getUserCommand(char* command , int num)    
{    
    printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());    
        
    // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf    
    char* r = fgets(command,num,stdin);    
    if(r == NULL) return -1;  // 读取失败返回-1    
    
    // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉    
    command[strlen(command)-1] = '\0';    
    // 由于读入字符串时,必定会有一个\n所以这么不可能会越界                                                                                            
                              
    // 用于测试输入的字符串是否符合我们的预期
    printf("%s",command);
    
    return 1;                 
}

int main()
{
    char usercommand[NUM]; 
    
    getUserCommand(usercommand,NUM);

    return 0;
}

二、分割字符串

2.1 设计

当我们获取到了命令字符串后,需要将字符串以空格为分隔符将字符串分割为子字符串,并将每一个子字符串的地址存入到一个指针数组中,这里给出一个字符串被分割的例子:"ls -l -a" -> "ls" "-l" "-a"。我们可以使用strtok函数来将字符串分割,使用strtok函数处理同一个字符串时,第一次需要传入字符串的地址,后面再次调用则只需要传入NULL即可。

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define NUM 1024                    
#define SIZE 64     
#define SEP " "

const char* getUsername()
{
    char* username = getenv("USER");
    if(username)
        return username;
    else
        return "none"; 
}

const char* getHostname()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname)
        return hostname;
    else
        return "none"; 
}


const char* getCwd()
{
    char* cwd = getenv("PWD");                                                                                                               
    if(cwd)                                                                                                                                  
        return cwd;                                                                                                                                      
    else                                                                                                                                     
        return "none";     
}


int main()  
{                                                                                                                                                        
    while(1)  
    {         
        char usercommand[NUM];  
        char* argv[SIZE];       
                                
        int x = getUserCommand(usercommand,NUM);  
                                                  
        if(x == -1) continue;                                
                                     
        int argc = 0;                
        argv[argc++] = strtok(usercommand,SEP);  


        while(argv[argc++] = strtok(NULL,SEP));          
        // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的    
    	// 所以分割的方式可以使用赋值作为循环判断条件进行循序    
    	// 若是大家不习惯可以使用下面这一种分割方式    
    	
        // while(argv[argc++] = strtok(NULL,SEP));  
        // while(1)                                    
        // {                                           
        //     argv[argc] = strtok(NULL,SEP);          
        //     if(argv[argc] == NULL)                  
        //         break;                      
        //     argc++;                         
        // }                                   
                                    
        for(int i = 0 ; argv[i] ; i++)  
        {                               
            printf("%d : %s \n",i,argv[i]);  
        }                                    
    }
    return 0;
}

2.2 封装

cpp 复制代码
void SplitCommand(char* in , char* out[])      
{      
    int argc = 0;      
    
    out[argc++] = strtok(in,SEP);      
    
    while(out[argc++] = strtok(NULL,SEP));        
    // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的    
    // 所以分割的方式可以使用赋值作为循环判断条件进行循序    
    // 若是大家不习惯可以使用下面这一种分割方式    
                                                                                                                                                       
    // while(1)    
    // {    
    //     out[argc] = strtok(NULL,SEP);    
    //     if(out[argc] == NULL)                                                                                                              
    //         break;    
    //     argc++;                                                                                                                            
    }                                                                                                                                                  
	                                                                                                                                    
// 用于测试字符串是否被分割                                                                                                                                             
#ifdef debug 
     for(int i = 0 ; out[i] ; i++)                                                                                                          
     {                                                                                                                                      
         printf("%d : %s \n",i,out[i]);                                                                                                     
     }        
#endif                                                                                                                                     
}                                                                                                                                          
                                                                                                                                           
int main()                                                                                                                                 
{                                                                                                                                          
    while(1)                                                                                                                               
    {                                                                                                                                      
        char usercommand[NUM];                                                                                                             
        char* argv[SIZE];    
            
        int x = getUserCommand(usercommand,NUM);    
    
        SplitCommand(usercommand,argv); 
    }
    return 0;
}

三、执行指令

3.1 设计

将命令字符串分割后,就需要执行命令了,我们知道bash需要一直运行,这里添加一个循环让他一直运行,我们可以使用前面学习过的进程替换来执行命令,但是不能使用当前进程来进程替换,当前进程还需要继续运行,所以我们可以创建一个子进程来执行命令,由于我们将字符串分割为子字符串存储在了指针数组中,这里可以使用execvp函数来进行进场替换。

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM 1024
#define SIZE 64 
#define SEP " " 
#define debug 1

const char* getUsername()
{
    char* username = getenv("USER");
    if(username)
        return username;
    else
        return "none"; 
}

const char* getHostname()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname)
        return hostname;
    else
        return "none"; 
}

const char* getCwd()
{
    char* cwd = getenv("PWD");                                                                                                               
    if(cwd)                                                                                                                                  
        return cwd;                                                                                                                                      
    else                                                                                                                                     
        return "none";     
}

int getUserCommand(char* command , int num)    
{    
    printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());    
        
    // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf    
    char* r = fgets(command,num,stdin);    
    if(r == NULL) return -1;  // 读取失败返回-1    
    
    // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉    
    command[strlen(command)-1] = '\0';    
    // 由于读入字符串时,必定会有一个\n所以这么不可能会越界    

	// 用于测试输入的字符串是否符合我们的预期
    // printf("%s",command);                                                                                        
                              
    return 1;                 
}

void SplitCommand(char* in , char* out[])      
{      
    int argc = 0;      
    
    out[argc++] = strtok(in,SEP);      
    
    while(out[argc++] = strtok(NULL,SEP));        
    // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的    
    // 所以分割的方式可以使用赋值作为循环判断条件进行循序    
    // 若是大家不习惯可以使用下面这一种分割方式    
                                                                                                                                                       
    // while(1)    
    // {    
    //     out[argc] = strtok(NULL,SEP);    
    //     if(out[argc] == NULL)                                                                                                              
    //         break;    
    //     argc++;                                                                                                                            
    }                                                                                                                                                  
	                                                                                                                                    
                                                                                                                                             
#ifdef debug 
     for(int i = 0 ; out[i] ; i++)                                                                                                          
     {                                                                                                                                      
         printf("%d : %s \n",i,out[i]);                                                                                                     
     }        
 #endif                                                                                                                                    
}  

int main()                                                                                                                                 
{                                                                                                                                          
    while(1)                                                                                                                               
    {                                                                                                                                                  
        char usercommand[NUM];                                                                                                             
        char* argv[SIZE];    
        // 打印命令提示符和获取命令行命令字符串    
        int x = getUserCommand(usercommand,NUM);    
    
        if(x <= 0) continue;    
    	// 分割命令字符串
        SplitCommand(usercommand,argv); 

        pid_t id = fork();

        if(id < 0) return -1;
        else if(id == 0)
        {
            execvp(argv[0],argv);
            // 若替换失败则子进程退出
            exit(-1);
        }
        else 
        {
            pid_t rid = wait(NULL);
            if(rid>0){};       
        }

    }
    return 0;
}

3.2 封装

cpp 复制代码
int execute(char* argv[])
{
    pid_t id = fork();

    if(id < 0) return -1;
    else if(id == 0)
    {    
        execvp(argv[0],argv);    
        // 若替换失败则子进程退出    
        exit(-1);    
    }    
    else    
    {    
        pid_t rid = wait(NULL);    
        if(rid>0){};     
    }    
    return 0;    
}    

int main()    
{    
    while(1)    
    {    
        char usercommand[NUM];    
        char* argv[SIZE];    
        // 打印命令提示符和获取命令行命令字符串        
        int x = getUserCommand(usercommand,NUM);    
                                                                                                                                                       
        if(x <= 0) continue;    
    	// 分割命令字符串
        SplitCommand(usercommand,argv); 
    	// 执行命令
        execute(argv);
    }
    return 0;
}

四、处理內键命令的执行

当我们使用上面的代码执行命令时,发现大部分命令都可以被执行,但是例如cd、export、echo这样的内建命令却不能被执行,原因是内建命令是作用与bash的也就是这里的父进程,并且内建命令是bash的一部分,与常见命令不同,执行内建命令时不需要创建新的子进程,所以这些内建命令需要被特殊处理一下。将命令字符串分割后就判断当前命令是否为内建命令,是则直接执行内建命令,否则认定为常见命令向下继续执行。内建命令如何处理,这里就不多讲解,详细处理方法在下面的代码中有详细的注释,有兴趣的可以看一下。

cpp 复制代码
#include <stdio.h>    
#include <string.h>    
#include <stdlib.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
    
#define NUM 1024    
#define SIZE 64     
#define SEP " "     
// #define debug 1    
    
// 由于环境变量PWD需要一直存在,这里定义一个全局变量来存储
char cwd[1024];    
// 定义一个全局二维数组中,用于存储添加的环境变量
char myenv[128][1024];    
// 记录二维数组中有多少个环境变量
int cnt = 0;    
int lastcode = 0; // 记录退出码    
                                                                                                                                                       
char* getHomename()    
{                                       
    char* homename = getenv("HOME");    
    if(homename)            
        return homename;    
    else                          
        return (char*)"none";    
}                                

const char* getUsername()    
{                                       
    char* username = getenv("USER");
    if(username)            
        return username;    
    else                                                                                                                                               
        return "none";    
}    

const char* getHostname()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname)
        return hostname;
    else
        return "none"; 
}

const char* getCwd()
{
    char* cwd = getenv("PWD");
    if(cwd)
        return cwd;
    else
        return "none"; 
}

int getUserCommand(char* command , int num)
{
    printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
    
    // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf                                                                         
    char* r = fgets(command,num,stdin);
    if(r == NULL) return -1;  // 读取失败返回-1
    // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
    command[strlen(command)-1] = '\0';
    // 由于读入字符串时,必定会有一个\n所以这么不可能会越界

    // 用于测试输入的字符串是否符合我们的预期   
    // printf("%s\n",command);    

    // 当命令行只输入回车时,则没有必要创建子进程来执行任务
    if(strlen(command) == 0)
        return 0;

    return 1;
}

void SplitCommand(char* in , char* out[])    
{    
    int argc = 0;    
    out[argc++] = strtok(in,SEP);    
  	while(out[argc++] = strtok(NULL,SEP));                                                                                                             
    // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的
    // 所以分割的方式可以使用赋值作为循环判断条件进行循序
    // 若是大家不习惯可以使用下面这一种分割方式
    

    //while(1)    
    //{    
    //    out[argc] = strtok(NULL,SEP);    
    //    if(out[argc] == NULL)                                                                                                              
    //        break;    
    //    argc++;                                                                                                                            
    //}                                                                                                                                      
#ifdef debug 
    for(int i = 0 ; out[i] ; i++)                                                                                                          
    {                                                                                                                                      
        printf("%d : %s \n",i,out[i]);                                                                                                     
    }        
#endif
}

int execute(char* argv[])
{
    pid_t id = fork();
                                                                                                                                                       
    if(id < 0) return -1;
    else if(id == 0)
    {
        execvp(argv[0],argv);
        // 若替换失败则子进程退出
        exit(-1);
    }
    else 
    {
        // sleep(1);
        int status = 0;
        pid_t rid = wait(&status);
        if(rid>0)
        {
            lastcode = WEXITSTATUS(status);
        }
        
    }
    return 0;
}

void cd(char* path)
{
	// 将当前的工作目录改为path
    chdir(path);
    // tmp作为一个临时空间
    char tmp[1024];
    // 将当前工作目录写入到tmp中
    getcwd(tmp,sizeof(tmp));
    // 将PWD=与tmp进行组合,形成环境变量的格式存储到全局变量cwd                                                                                                                     
    sprintf(cwd,"PWD=%s",tmp);
    // 将cwd添加到环境变量中,覆盖掉原来的环境变量PWD
    putenv(cwd);
}

// 内键命令并执行1,非内键命令0
int dobuildin(char* argv[])
{
    if(strcmp(argv[0],"cd") == 0)
    {
        char* path = NULL;
        // cd命令后面没有添加路径,默认更改当前工作目录为家目录
        if(argv[1] == NULL)
            path = getHomename();
        else 
            path = argv[1];

        cd(path);
        return 1;
    }
    else if(strcmp(argv[0],"export") == 0)
    {
    	// 如果export后面没有内容则不做任何处理
        if(argv[1] == NULL)
            return 1;
        else 
        {
        	// 将需要添加的环境变量保存到全局的二维数组中
            strcpy(myenv[cnt],argv[1]);
            // 将这个环境变量添加到进程的环境变量表中
            putenv(myenv[cnt++]);
            return 1;
        }
    }
	else if(strcmp(argv[0],"echo") == 0)
    {
    	// 当echo后面没有内容时,默认输出回车
        if(argv[1] == NULL)
        {
            printf("\n");
            return 1;
        }
        // 当echo后面的字符串的第一个字符为$时
        // 就是查看进程的退出码或是环境变量的                                                                                                                                              
        else if(*(argv[1]) == '$' && strlen(argv[1]) >= 2)
        {
            // 输出退出码
            if(*(argv[1]+1) == '?')
            {
                printf("%d\n",lastcode);            
                lastcode = 0;
            }
            else  // 输出环境变量  
            {
                const char* enval = getenv(argv[1]+1);
                if(enval)
                {
                    printf("%s",enval);
                }
                else 
                {
                    printf("\n");
                }
            }
        }
        // 不符合上面情况,通常就是将echo后面的字符串直接输出
        else 
        {
            printf("%s",argv[1]);
        }
        return 1;
	}                                                                                                                                                  
    else if(0){}
    return 0;
}

int main()                                                                                                                                 
{                                                                                                                                          
    while(1)                                                                                                                               
    {                                                                                                                                      
        char usercommand[NUM];                                                                                                             
        char* argv[SIZE];    
        // 打印命令提示符和获取命令行命令字符串    
        int x = getUserCommand(usercommand,NUM);    
    
        if(x <= 0) continue;    
    
        SplitCommand(usercommand,argv); 

        x = dobuildin(argv);
        if(x == 1)
            continue;

        execute(argv);
    }
    return 0;
}

五、重定向(本文章所有代码)

写完前面的代码后,发现这个代码并不能解决重定向的问题,没了解重定向的最好看一下后面一篇文章了解一下重定向是什么,在分割命令字符串之前,我们需要判断这个命令字符串是否需要进行重定向,需要重定向则需要对字符串进行处理,例如"ls -l -a > fortest.txt" -> "ls -l -a" 重定向类型 "fortest.txt",我们会得到三个部分,命令字符串、重定向类型、和文件名,在代码定义四种重定向类型,无重定向、输入重定向、输出重定向和追加重定向,默认情况下是无重定向,定义一个全局变量存储重定向类型,定义一个全局指针来指向文件名,然后我们针对不同的重定向类型使用dup2函数进行不同的处理,详细不同重定向的处理过程请查看代码中重定向的一部分。

cpp 复制代码
#include <stdio.h>                                                                                                                                     
#include <string.h>                   
#include <stdlib.h>    
#include <unistd.h>    
#include <sys/types.h>                         
#include <sys/wait.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <ctype.h>    
     
#define NUM 1024    
#define SIZE 64        
#define SEP " "     
// #define debug 1    
                                                            
#define NoneRedir   0           
#define InputRedir  1             
#define OutputRedir 2    
#define AppendRedir 3    
    
int redir = NoneRedir;             
char* filename = NULL;         
     
// 由于环境变量PWD需要一直存在,这里定义一个全局变量来存储
char cwd[1024];    
// 定义一个全局二维数组中,用于存储添加的环境变量
char myenv[128][1024];    
// 记录二维数组中有多少个环境变量
int cnt = 0;    
int lastcode = 0; // 记录退出码   

char* getHomename()
{                              
    char* homename = getenv("HOME");    
    if(homename)
        return homename;    
    else    
        return (char*)"none";                                                                                                                          
}

const char* getUsername()
{
    char* username = getenv("USER");
    if(username)
        return username;
    else
        return "none"; 
}

const char* getHostname()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname)
        return hostname;
    else
        return "none"; 
}
const char* getCwd()
{
    char* cwd = getenv("PWD");                                                                                                                         
    if(cwd)
        return cwd;
    else
        return "none"; 
}

int getUserCommand(char* command , int num)
{
    printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
    
    // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf
    char* r = fgets(command,num,stdin);
    if(r == NULL) return -1;  // 读取失败返回-1
    // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
    command[strlen(command)-1] = '\0';
    // 由于读入字符串时,必定会有一个\n所以这么不可能会越界

    // 用于测试输入的字符串是否符合我们的预期   
    // printf("%s\n",command);    

    // 当命令行只输入回车时,则没有必要创建子进程来执行任务
    if(strlen(command) == 0)
        return 0;

    return 1;
}

void SplitCommand(char* in , char* out[])    
{    
    int argc = 0;    
    out[argc++] = strtok(in,SEP);    
	while(out[argc++] = strtok(NULL,SEP));    
                                                                                                                                 
#ifdef debug 
    for(int i = 0 ; out[i] ; i++)                                                                                                          
    {                                                                                                                                      
        printf("%d : %s \n",i,out[i]);                                                                                                     
    }        
#endif
}  
int execute(char* argv[])
{
    pid_t id = fork();

    if(id < 0) return -1;                                                                                                                              
    else if(id == 0)
    {
        int fd = 0;
        if(redir == InputRedir)
        {
            fd = open(filename,O_RDONLY);
            dup2(fd,0);
        }
        else if(redir == OutputRedir)
        {
            fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
            dup2(fd,1);
        }
        else if(redir == AppendRedir)
        {
            fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
            dup2(fd,1);
        }
        else 
        {
            // do nothing
        }

        execvp(argv[0],argv);
        // 若替换失败则子进程退出
        exit(-1);
    }
	else 
    {
        // sleep(1);
        int status = 0;
        pid_t rid = wait(&status);
        if(rid>0)
        {
            lastcode = WEXITSTATUS(status);
        }
        
    }
    return 0;
}

void cd(char* path)
{
    chdir(path);
    char tmp[1024];
    // char* tmp = getenv("PWD");
    getcwd(tmp,sizeof(tmp));
    sprintf(cwd,"PWD=%s",tmp);
    putenv(cwd);
}

// 内键命令并执行1,非内键命令0
int dobuildin(char* argv[])
{
    if(strcmp(argv[0],"cd") == 0)
    {
        char* path = NULL;
        if(argv[1] == NULL)
            path = getHomename();
        else 
            path = argv[1];

        cd(path);                                                                                                                                      
        return 1;
    }
    else if(strcmp(argv[0],"export") == 0)
    {
        if(argv[1] == NULL)
            return 1;
        else 
        {
            strcpy(myenv[cnt],argv[1]);
            putenv(myenv[cnt++]);
            return 1;
        }
    }
	else if(strcmp(argv[0],"echo") == 0)
    {                                                                                                                                                  
        if(argv[1] == NULL)
        {
            printf("\n");
            return 1;
        }
        else if(*(argv[1]) == '$' && strlen(argv[1]) >= 2)
        {
            // 输出退出码
            if(*(argv[1]+1) == '?')
            {
                printf("%d\n",lastcode);
                lastcode = 0;
            }
            else  // 输出环境变量  
            {
                const char* enval = getenv(argv[1]+1);
                if(enval)
                {
                    printf("%s",enval);
                }
                else 
                {
                    printf("\n");
                }
            }
        }
        else 
        {
            printf("%s",argv[1]);
        }
        return 1;
    }
    else if(0){}
    return 0;
}

// 用于跳过空格
#define SkipSpace(pos) do{while(isspace(*pos)) pos++;}while(0)

// 判断是否为重定向
void CheckRedir(char command[])
{
    char* start = command;                                                                                                                             
    char* end = command +  strlen(command);

    while(start < end)
    {
        // 输入重定向
        if(*end == '<')
        {
            *end = '\0';
            filename = end + 1;
            SkipSpace(filename);
            redir = InputRedir;
        }
        else if(*end == '>')
        {
            // 追加重定向
            if(*(end-1) == '>')
            {
                *(end-1) = '\0';
                filename = end + 1;
                SkipSpace(filename);
                redir = AppendRedir;
            }
            // 输出重定向
            else 
            {
                *end = '\0';
                filename = end + 1;                                                                                                                    
                SkipSpace(filename);
                redir = OutputRedir;
            }
        }
        end--;
    }
}

int main()                                                                                                                                 
{                                                                                                                                           
    while(1)                                                                                                                               
    {                                                                                                                                                  
        // 初始默认为非重定向
        redir = NoneRedir;
        filename = NULL;

        char usercommand[NUM];                                                                                                             
        char* argv[SIZE];    
        // 打印命令提示符和获取命令行命令字符串    
        int x = getUserCommand(usercommand,NUM);    
    
        if(x <= 0) continue;    
    
        // 判断是否为重定向
        CheckRedir(usercommand);

        // 分割命令行  "ls -a -l" -> "ls" "-a" "-l"
        SplitCommand(usercommand,argv); 

        // 判断是否为內键命令
        x = dobuildin(argv);
        if(x == 1)
            continue;

        // 执行指令
        execute(argv);
    }
    return 0;
}

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。

希望大家以后也能和我一起进步!!🌹🌹

如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

相关推荐
孙克旭_1 小时前
PXE_Kickstart_无人值守自动化安装系统
linux·运维·自动化
皓月盈江2 小时前
Linux电脑本机使用小皮面板集成环境开发调试WEB项目
linux·php·web开发·phpstudy·小皮面板·集成环境·www.xp.cn
深井冰水2 小时前
mac M2能安装的虚拟机和linux系统系统
linux·macos
leoufung3 小时前
内核内存锁定机制与用户空间内存锁定的交互分析
linux·kernel
菜菜why4 小时前
AutoDL租用服务器教程
服务器
IT专业服务商4 小时前
联想 SR550 服务器,配置 RAID 5教程!
运维·服务器·windows·microsoft·硬件架构
忧虑的乌龟蛋4 小时前
嵌入式Linux I2C驱动开发详解
linux·驱动开发·嵌入式·iic·i2c·读数据·写数据
I_Scholar5 小时前
OPENSSL-1.1.1的使用及注意事项
linux·ssl
Johny_Zhao5 小时前
K8S+nginx+MYSQL+TOMCAT高可用架构企业自建网站
linux·网络·mysql·nginx·网络安全·信息安全·tomcat·云计算·shell·yum源·系统运维·itsm
稳联技术5 小时前
Ethercat转Profinet网关如何用“协议翻译术“打通自动化产线任督二脉
linux·服务器·网络