【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;
}

结尾

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

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

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

相关推荐
爱敲代码的边芙10 分钟前
Linux:信号的保存[2]
linux·运维·服务器
葛小白112 分钟前
第五天 Labview数据记录(5.1 INI配置文件读写)
服务器·labview
工程师焱记23 分钟前
Linux 常用命令——系统设置篇(保姆级说明)
linux·运维·服务器
某风吾起44 分钟前
linux系统中的 scp的使用方法
linux·服务器·网络
『往事』&白驹过隙;1 小时前
操作系统(Linux Kernel 0.11&Linux Kernel 0.12)解读整理——内核初始化(main & init)之缓冲区的管理
linux·c语言·数据结构·物联网·操作系统
chian-ocean1 小时前
探索Linux中的进程控制:从启动到退出的背后原理
linux·运维·服务器
涛ing1 小时前
23. C语言 文件操作详解
java·linux·c语言·开发语言·c++·vscode·vim
阿猿收手吧!1 小时前
【Linux网络总结】字节序转换 收发信息 TCP握手挥手 多路转接
linux·服务器·网络·c++·tcp/ip
华纳云IDC服务商1 小时前
常见的备份服务器操作系统如何选择
运维·服务器
m0_748233641 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php