最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux之簡(jiǎn)易Linux?Shell的實(shí)現(xiàn)過程

 更新時(shí)間:2026年04月22日 09:04:33   作者:碼完就睡  
本文通過C語言實(shí)現(xiàn)一個(gè)簡(jiǎn)易命令行解釋器,解析用戶輸入的指令,處理內(nèi)置命令,并創(chuàng)建子進(jìn)程執(zhí)行外部命令,文章詳細(xì)介紹了命令提示符、命令解析及主函數(shù)實(shí)現(xiàn)邏輯

一、引言

在使用 Linux 終端時(shí),我們輸入一條命令(例如 ls -l)后,終端就會(huì)解析并執(zhí)行對(duì)應(yīng)的指令,最終將結(jié)果展示出來。

為了理解這一過程背后的實(shí)現(xiàn)原理,本文將通過C語言代碼,手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的命令行解釋器,模擬 Shell 的核心執(zhí)行邏輯。

引言

二、核心模塊分析

(一)打印命令提示符

1.區(qū)分普通用戶和管理員

普通用戶使用 $ 作為提示符;管理員使用 # 作為提示符

用戶ID = 0 ,則說明當(dāng)前是管理員。

1.區(qū)分普通用戶和管理員

/*區(qū)分普通用戶和管理員*/
char *str = "$"; //初始默認(rèn)為普通用戶,使用 $
int uid = getuid(); //獲取當(dāng)前用戶ID
if(uid == 0) str = "#"; //若 用戶ID = 0, 則為管理員, 使用 #

2.獲取用戶信息、主機(jī)名稱、當(dāng)前路徑

/*獲取用戶信息、主機(jī)名稱、當(dāng)前路徑*/
struct passwd *ptr = getpwuid(uid); //獲取用戶信息

//如果獲取用戶信息失敗,就打印一個(gè)簡(jiǎn)單的提示符 mybash $,直接退出函數(shù)
if(ptr == NULL)
{
    printf("mybash $");
    fflush(stdout);
    return;
}

char host[128] = {0};
gethostname(host, 128); //獲取主機(jī)名稱
    
char path[128] = {0};
getcwd(path, 128); //獲取當(dāng)前路徑

3.打印

3.打印

注意:獲取到的用戶信息都在 ptr 這個(gè)結(jié)構(gòu)體中,pw_name 就是用戶名。

/*打印*/
printf("%s@%s:%s%s", ptr->pw_name, host, path, str);
fflush(stdout); //刷新輸出緩沖區(qū)

4.完整代碼

/*打印命令提示符*/
void printf_info()
{
    /*區(qū)分普通用戶和管理員*/
    char *str = "$"; //初始默認(rèn)為普通用戶,使用 $
    int uid = getuid(); //獲取當(dāng)前用戶ID
    if(uid == 0) str = "#"; //若 用戶ID = 0, 則為管理員, 使用 #

    /*獲取用戶信息、主機(jī)名稱、當(dāng)前路徑*/
    struct passwd *ptr = getpwuid(uid); //獲取用戶信息

    //如果獲取用戶信息失敗,就打印一個(gè)簡(jiǎn)單的提示符 mybash $,直接退出函數(shù)
    if(ptr == NULL)
    {
        printf("mybash $");
        fflush(stdout);
        return;
    }

    char host[128] = {0};
    gethostname(host, 128); //獲取主機(jī)名稱
    
    char path[128] = {0};
    getcwd(path, 128); //獲取當(dāng)前路徑

    /*打印*/
    printf("%s@%s:%s%s", ptr->pw_name, host, path, str);
    fflush(stdout); //刷新輸出緩沖區(qū)
}

(二)命令解析

在Shell中,用戶輸入的一條完整指令通常由命令、選項(xiàng)、參數(shù)共同組成。

為了讓程序能夠識(shí)別并執(zhí)行指令,我們需要對(duì)輸入的整行字符串按空格進(jìn)行分割,將其拆解為獨(dú)立的命令與參數(shù)列表,供后續(xù)執(zhí)行使用。

strtok():字符串分割函數(shù);可以按照規(guī)定的方式分割字符串

/*命令解析*/
char *get_cmd(char buff[], char *myargv[])
{
    int i = 0;
    char *s = strtok(buff, " "); //按空格分隔第一個(gè)單詞
    while(s != NULL) //當(dāng)分割出來的字符串為NULL時(shí),說明分割完成,循環(huán)結(jié)束
    {
        myargv[i++] = s; //將分割出來的字符串放進(jìn)myargv數(shù)組中
        s = strtok(NULL, " "); //NULL會(huì)繼續(xù)分割剩余的字符串
    }
    
    myargv[i] = NULL; //讓myargv以NULL結(jié)尾,避免后續(xù)使用execvp錯(cuò)誤

    return myargv[0]; //返回第一個(gè)單詞,即命令名(如 ls、cd)
}

(三)主函數(shù)

1.打印命令提示符

/*打印命令提示符*/
printf_info(); 

2.讀取用戶輸入

/*讀取用戶輸入*/
char buff[128] = {0};
fgets(buff, 128, stdin); //讀取用戶輸入
buff[strlen(buff) - 1] = '\0'; //去掉末尾換行

3.解析命令

/*解析命令*/
char *myargv[MAX_SIZE] = {0};
char *cmd = get_cmd(buff, myargv); //分割命令,cmd是返回的是輸入的命令名(如 ls、cd)

4.處理空命令和內(nèi)置命令

cd 是 Shell 內(nèi)置命令,不能通過創(chuàng)建子進(jìn)程執(zhí)行;

chdir()是 Linux 系統(tǒng)調(diào)用,用于切換當(dāng)前工作目錄。

/*處理空命令和內(nèi)置命令*/
if(cmd == NULL) continue; //輸入為空,重新循環(huán)

if(strcmp(cmd, "exit") == 0) exit(0); //輸入exit,退出

if(strcmp(cmd, "cd") == 0) 
{
    chdir(myargv[1]); //需要使用chdir()函數(shù)進(jìn)行跳轉(zhuǎn)
    continue;
}

5.創(chuàng)建子進(jìn)程

/*創(chuàng)建子進(jìn)程*/
pid_t pid = fork();
if(pid == -1) exit(0); //創(chuàng)建失敗,直接退出

6.子進(jìn)程執(zhí)行外部命令

  • int execvp(const *file, char *const argv[]);
  • file:要執(zhí)行的程序名
  • argv:參數(shù)數(shù)組,第一個(gè)元素通常是程序名,最后一個(gè)元素必須是NULL
/*子進(jìn)程執(zhí)行外部命令*/
if(pid == 0)  //子進(jìn)程
{
    execvp(cmd, myargv); //執(zhí)行命令
    //執(zhí)行成功后,子進(jìn)程會(huì)完全變成目標(biāo)命令,不會(huì)再往下執(zhí)行

    printf("exec err!!!\n");
    exit(1);
}

7.父進(jìn)程等待子進(jìn)程

/*父進(jìn)程等待子進(jìn)程*/
else wait(1); //父進(jìn)程,等待子進(jìn)程結(jié)束

8.完整代碼

/*主函數(shù)*/
int main()
{
    while(1) 
    {
        /*打印命令提示符*/
        printf_info(); 

        /*讀取用戶輸入*/
        char buff[128] = {0};
        fgets(buff, 128, stdin); //讀取用戶輸入
        buff[strlen(buff) - 1] = '\0'; //去掉末尾換行
        
        /*解析命令*/
        char *myargv[MAX_SIZE] = {0};
        char *cmd = get_cmd(buff, myargv); //分割命令,cmd是返回的是輸入的命令名(如 ls、cd)
        
        /*處理空命令和內(nèi)置命令*/
        if(cmd == NULL) continue; //輸入為空,重新循環(huán)

        if(strcmp(cmd, "exit") == 0) exit(0); //輸入exit,退出

        if(strcmp(cmd, "cd") == 0) 
        {
            chdir(myargv[1]); //需要使用chdir()函數(shù)進(jìn)行跳轉(zhuǎn)
            continue;
        }

        /*創(chuàng)建子進(jìn)程*/
        pid_t pid = fork();
        if(pid == -1) exit(0); //創(chuàng)建失敗,直接退出

        /*子進(jìn)程執(zhí)行外部命令*/
        if(pid == 0)  //子進(jìn)程
        {
            execvp(cmd, myargv); //執(zhí)行命令
            //執(zhí)行成功后,子進(jìn)程會(huì)完全變成目標(biāo)命令,不會(huì)再往下執(zhí)行

            printf("exec err!!!\n");
            exit(1);
        }

        /*父進(jìn)程等待子進(jìn)程*/
        else wait(1); //父進(jìn)程,等待子進(jìn)程結(jié)束
    }
}

三、測(cè)試

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <pwd.h>

#define MAX_SIZE 100

/*打印命令提示符*/
void printf_info()
{
    /*區(qū)分普通用戶和管理員*/
    char *str = "$"; //初始默認(rèn)為普通用戶,使用 $
    int uid = getuid(); //獲取當(dāng)前用戶ID
    if(uid == 0) str = "#"; //若 用戶ID = 0, 則為管理員, 使用 #

    /*獲取用戶信息、主機(jī)名稱、當(dāng)前路徑*/
    struct passwd *ptr = getpwuid(uid); //獲取用戶信息

    //如果獲取用戶信息失敗,就打印一個(gè)簡(jiǎn)單的提示符 mybash $,直接退出函數(shù)
    if(ptr == NULL)
    {
        printf("mybash $");
        fflush(stdout);
        return;
    }

    char host[128] = {0};
    gethostname(host, 128); //獲取主機(jī)名稱
    
    char path[128] = {0};
    getcwd(path, 128); //獲取當(dāng)前路徑

    /*打印*/
    printf("%s@%s:%s%s ", ptr->pw_name, host, path, str);
    fflush(stdout); //刷新輸出緩沖區(qū)
}



/*命令解析*/
char *get_cmd(char buff[], char *myargv[])
{
    int i = 0;
    char *s = strtok(buff, " "); //按空格分隔第一個(gè)單詞
    while(s != NULL) //當(dāng)分割出來的字符串為NULL時(shí),說明分割完成,循環(huán)結(jié)束
    {
        myargv[i++] = s; //將分割出來的字符串放進(jìn)myargv數(shù)組中
        s = strtok(NULL, " "); //NULL會(huì)繼續(xù)分割剩余的字符串
    }
    
    myargv[i] = NULL; //讓myargv以NULL結(jié)尾,避免后續(xù)使用execvp錯(cuò)誤

    return myargv[0]; //返回第一個(gè)單詞,即命令名(如 ls、cd)
}


/*主函數(shù)*/
int main()
{
    while(1) 
    {
        /*打印命令提示符*/
        printf_info(); 

        /*讀取用戶輸入*/
        char buff[128] = {0};
        fgets(buff, 128, stdin); //讀取用戶輸入
        buff[strlen(buff) - 1] = '\0'; //去掉末尾換行
        
        /*解析命令*/
        char *myargv[MAX_SIZE] = {0};
        char *cmd = get_cmd(buff, myargv); //分割命令,cmd是返回的是輸入的命令名(如 ls、cd)
        
        /*處理空命令和內(nèi)置命令*/
        if(cmd == NULL) continue; //輸入為空,重新循環(huán)

        if(strcmp(cmd, "exit") == 0) exit(0); //輸入exit,退出

        if(strcmp(cmd, "cd") == 0) 
        {
            chdir(myargv[1]); //需要使用chdir()函數(shù)進(jìn)行跳轉(zhuǎn)
            continue;
        }

        /*創(chuàng)建子進(jìn)程*/
        pid_t pid = fork();
        if(pid == -1) exit(0); //創(chuàng)建失敗,直接退出

        /*子進(jìn)程執(zhí)行外部命令*/
        if(pid == 0)  //子進(jìn)程
        {
            execvp(cmd, myargv); //執(zhí)行命令
            //執(zhí)行成功后,子進(jìn)程會(huì)完全變成目標(biāo)命令,不會(huì)再往下執(zhí)行

            printf("exec err!!!\n");
            exit(1);
        }

        /*父進(jìn)程等待子進(jìn)程*/
        else wait(1); //父進(jìn)程,等待子進(jìn)程結(jié)束
    }
}

測(cè)試

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

娱乐| 香港| 凤凰县| 高尔夫| 武平县| 南和县| 贡嘎县| 新乐市| 犍为县| 裕民县| 滁州市| 荥经县| 论坛| 日照市| 南华县| 奇台县| 香河县| 乐安县| 屏南县| 龙海市| 仁布县| 景东| 西和县| 奇台县| 六枝特区| 浦东新区| 亳州市| 绍兴市| 新疆| 广水市| 河南省| 巩留县| 林州市| 天祝| 仙桃市| 化德县| 台湾省| 泗水县| 临汾市| 巧家县| 辽宁省|