shell的模擬實現 ─── linux第16課

在shell的命令行中輸入命令,會有兩種執行命令的途徑

  1. ? ? ? ? shell自己執行
  2. ? ? ? ? shell創建子進程(fork ,exit ,waitpid,exec) ,子進程去執行

shell自己執行的命令是自建命令(bulit command)

子進程執行的是非自建命令

第一版只能維護命令行參數表+創建子進程, 執行非內建命令

????????我們先創建了命令行提示符 ,獲取了命令行的內容,維護了命令行參數表,創建了子進程進行命令的執行

?1 #include<iostream>2 #include<cstdio>3 #include<stdlib.h>4 #include<cstring>5 #include<string>6 #include<unistd.h>7 #include<sys/types.h>8 #include<sys/wait.h>9 10 using namespace std;11 const int charsize =1024;12 const int gargvnum =64;13 14 //全局的15 char* gargv[gargvnum];16 int gargc;17 18 19 int lastcode = 0;20 21 string GetUsrName()22 {23     string name =getenv("USER");24     return name.empty()?  "None" : name;25 }26 string GetHostName()27 {28     string name =getenv("HOSTNAME");29     return name.empty()?  "None" : name;30 }31 string GetPwd()32 {33     string name =getenv("PWD");34     return name.empty()?  "None" : name;35 }36 string MakeCommandLine()37 {  //[root@hcss-ecs-1f3a lesson17]#  38     char CommandLine[charsize];39     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\40         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());41     return CommandLine;42 }43 void PrintCommandLine()//1.打印命令行提示符44 {45     printf("%s",MakeCommandLine().c_str());46     fflush(stdout);47 }48 49 50 bool GetCommand(char Command_buff[] ,int size)//2.獲取命令51 {52     //將命令輸出到字符數組中53     //ls -a -l -n54     char*result =fgets(Command_buff,size,stdin);55     if(!result)56     {57         return false;58     }59     Command_buff[strlen(Command_buff)-1]= 0;//fgets會將回車(\n)也輸入60     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)會停下來61     return true;62 }63 64  void ParseCommand(char Command_buff[] ,int size)//3.分析命令65 {66     memset(gargv ,0,sizeof(gargv));67     gargc=0;68     const char* SEP =" ";69     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 會在字符串中查找分隔符,并將分隔符替換為 \0,從而將字符串分割成多個70     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后續使用要用nullptr代替元字符串71     gargc--;72 }73 74 void debug()75 {76     printf("argc: %d\n", gargc);77     for(int i = 0; gargv[i]; i++)78     {79         printf("argv[%d]: %s\n", i, gargv[i]);80     }81 }85 bool ExecuteCommand()//4.執行命令86 {87     pid_t id =fork();88     if(id ==0)89     {//子進程90         int ret =execvp(gargv[0],gargv);91         if(ret ==-1) cout<<"子進程出錯\n"<<endl;92         exit(1);93     }94     int status =0;95     pid_t rid =waitpid(id,&status ,0);96     if(rid >0)97     {98         if(WIFEXITED(status))99         {
100            lastcode=WEXITSTATUS(status);
101         }
102         else lastcode =100;
103         return true;
104     }
105     return false;
106 
107 }
108 
109 int main()
110 {
111     char Command_buff[charsize];
112     while(true)
113     {
114         PrintCommandLine();//1.打印命令行提示符
115 
116         if(!GetCommand(Command_buff,charsize))//2.獲取命令
117         {
118             continue;
119         }
120         ParseCommand(Command_buff ,charsize);//3.分析命令
121         //debug();
122         ExecuteCommand();//4.執行命令
123     }
124     return 0;
125 }?

第一版的執行結果:

  • 第一版在執行cd ..時,是改變了子進程的cwd,子進程執行完又退了,無法影響下面的進程,cd..?不能讓子進程執行.
  • 所以cd ..?或cd / 是自建命令 ,需要shell自己執行,以便可以影響到下面的進程(例如在/?目錄下創建文件)

?第二版能維護命令行參數表+執行cd命令 ,判斷了是否是自建命令(mysell自己執行自建命令,可以對環境變量發生改變),子進程執行其他命令.

在執行創建子進程前判斷,命令是否是內建命令(是否是cd?命令),是否要創建子進程.

getpwd不再是從環境變量中拿,從pcb中拿(因為pcb中是實時的),拿完更新環境變量(命令行提示符每次運行都會刷新,借此來維護cd?后的環境變量).

    #include<iostream>2 #include<cstdio>3 #include<stdlib.h>4 #include<cstring>5 #include<string>6 #include<unistd.h>7 #include<sys/types.h>8 #include<sys/wait.h>9 10 using namespace std;11 const int charsize =1024;12 const int gargvnum =64;14 //全局的15 char* gargv[gargvnum];16 int gargc;17 19 20 21 //全局的當前shell的工作路徑(定義到全局不會被銷毀)22 char pwd[charsize];23 char pwdenv[charsize];24 25 int lastcode = 0;26 27 string GetUsrName()28 {29     string name =getenv("USER");30     return name.empty()?  "None" : name;31 }32 string GetHostName()33 {34     string name =getenv("HOSTNAME");35     return name.empty()?  "None" : name;36 }string GetPwd()38 {39     //string name =getenv("PWD");40     //return name.empty()?  "None" : name;41 42 43     //從pcb中直接拿pwd44     if(nullptr ==  getcwd(pwd,sizeof(pwd))) return "None";45     //拿到后還需要更新環境變量中的pwd46     snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);47     putenv(pwdenv);48     return pwd;49 50 51 }52 string MakeCommandLine()53 {  //[root@hcss-ecs-1f3a lesson17]#  54     char CommandLine[charsize];55     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\56         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());57     return CommandLine;58 }59 void PrintCommandLine()//1.打印命令行提示符60 {61     printf("%s",MakeCommandLine().c_str());62     fflush(stdout);63 }64 65 66 bool GetCommand(char Command_buff[] ,int size)//2.獲取命令67 {68     //將命令輸出到字符數組中69     //ls -a -l -n70     char*result =fgets(Command_buff,size,stdin);71     if(!result)72     {73         return false;74     }75     Command_buff[strlen(Command_buff)-1]= 0;//fgets會將回車(\n)也輸入76     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)會停下來77     return true;78 }80  void ParseCommand(char Command_buff[] ,int size)//3.分析命令81 {82     memset(gargv ,0,sizeof(gargv));83     gargc=0;84     const char* SEP =" ";85     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 會在字符串中查找分隔符,并將分隔符替換為 \0,從而將字符串分割成多個86     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后續使用要用nullptr代替元字符串87     gargc--;88 }89 90 void debug()91 {92     printf("argc: %d\n", gargc);93     for(int i = 0; gargv[i]; i++)94     {95         printf("argv[%d]: %s\n", i, gargv[i]);96     }97 }98 99 
100 
101 bool ExecuteCommand()//4.執行命令
102 {
103     pid_t id =fork();
104     if(id ==0)
105     {//子進程
106         int ret =execvp(gargv[0],gargv);
107         if(ret ==-1) cout<<"子進程出錯\n"<<endl;
108         exit(1);
109     }
110     int status =0;
111     pid_t rid =waitpid(id,&status ,0);
112     if(rid >0)
113     {
114         if(WIFEXITED(status))
115         {
116            lastcode=WEXITSTATUS(status);
117         }
118         else lastcode =100;
119         return true;
120     }return false;
122 
123 }
124 
125 //內建命令的執行(調用函數,改變狀態)
126 bool CheckandExecBuiltCommand()
127 {   //使用窮舉法找內建命令
128     if(0 == strcmp(gargv[0],"cd"))
129     {
130         if(gargc == 2)
131         {
132             chdir(gargv[1]);
133         }
134         return true;
135     }
136     return false;
137 
138 }
139 
140 int main()
141 {
142     char Command_buff[charsize];
143     while(true)
144     {
145         PrintCommandLine();//1.打印命令行提示符
146 
147         if(!GetCommand(Command_buff,charsize))//2.獲取命令
148         {
149             continue;
150         }
151         ParseCommand(Command_buff ,charsize);//3.分析命令
152         //debug();
153         //判斷是否是內建命令,shell自己執行
154         if(CheckandExecBuiltCommand())//是內建命令并執行
155         {
156             continue;
157         }
158         //不是內建命令,創建子進程執行
159         ExecuteCommand();//4.執行命令
160     }
161     return 0;
162 }

第二版執行結果:

第三版?模擬真實shell從系統文件中獲取環境變量,維護命令行參數表+維護環境變量表(execvpe)

  • myshell前面的兩版都是從系統shell中獲取的環境變量表
  • 實際上,系統shell開啟時,是從系統文件中獲取環境變量表,但是這個過程涉及shell腳本(比較難搞,意義不大),
  • 所以我們用將系統shell的環境變量表手動拷貝到myshell中的過程來模擬系統shell開啟時,是從系統文件中獲取環境變量表

  • 要讓myshell執行的子進程的環境變量與myshell一致(不與系統shell一致)使用execvpe
  • 系統的shell維護了兩張表(命令行參數表+環境變量表)

第三版拷貝了環境變量表,維護了環境變量表(exxcvpe),增加了內置命令

第三版運行結果:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72903.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72903.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72903.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL創建數據庫和表,插入四大名著中的人物

一、登錄數據庫并創建數據庫db_ck 二、創建表t_hero 表屬性包括&#xff08;id&#xff0c;name&#xff0c;nickname&#xff0c;age&#xff0c;gender&#xff0c;address&#xff0c;weapon&#xff0c;types&#xff09; mysql> create table t_hero(-> id int,-…

靜態網頁的爬蟲(以電影天堂為例)

一、電影天堂的網址&#xff08;url&#xff09; 電影天堂_免費電影_迅雷電影下載_電影天堂網最好的迅雷電影下載網&#xff0c;分享最新電影&#xff0c;高清電影、綜藝、動漫、電視劇等下載&#xff01;https://dydytt.net/index.htm 我們要爬取這個頁面上的內容 二、代碼…

【C++】:STL詳解 —— 紅黑樹封裝map和set

目錄 紅黑樹的源代碼 正向迭代器的代碼 反向迭代器的代碼 set的模擬實現 map的模擬實現 紅黑樹的源代碼 #pragma once #include <iostream>using namespace std; // set ->key // map ->key/value// set ->key // map ->key/valueenum Colour {RED,BLAC…

MATLAB控制函數測試要點剖析

一、功能準確性檢驗 基礎功能核驗 針對常用控制函數&#xff0c;像用于傳遞函數建模的 tf 、構建狀態空間模型的 ss &#xff0c;以及開展階躍響應分析的 step 等&#xff0c;必須確認其能精準執行基礎操作。以 tf 函數為例&#xff0c;在輸入分子與分母系數后&#xff0c;理…

MoonSharp 文檔一

目錄 1.Getting Started 步驟1&#xff1a;在 IDE 中引入 MoonSharp 步驟2&#xff1a;引入命名空間 步驟3&#xff1a;調用腳本 步驟4&#xff1a;運行代碼 2.Keeping a Script around 步驟1&#xff1a;復現前教程所有操作 步驟2&#xff1a;改為創建Script對象 步驟…

ROS云課三分鐘-差動移動機器人導航報告如何撰寫-及格邊緣瘋狂試探

提示詞&#xff1a;基于如上所有案例并結合roslaunch teb_local_planner_tutorials robot_diff_drive_in_stage.launch和上面所有對話內容&#xff0c;設計一個差速移動機器人仿真實驗&#xff0c;并完成報告的全文撰寫。 差速移動機器人導航仿真實驗報告 一、實驗目的 驗證 T…

ACE協議學習1

在多核系統或復雜SoC&#xff08;System on Chip&#xff09;中&#xff0c;不同處理器核心或IP&#xff08;Intellectual Property&#xff09;模塊之間需要保持數據的一致性。常用的是ACE協議or CHI。 先對ACE協議進行學習 ACE協議&#xff08;Advanced Microcontroller Bu…

ajax之生成一個ajax的demo示例

目錄 一. node.js和express ?二. 使用express創建后端服務 三. 創建前端 一. node.js和express ajax是前端在不刷新的情況下訪問后端的技術&#xff0c;所以首先需要配置一個后端服務&#xff0c;可以使用node.js和express。 首先生成一個空項目&#xff0c;新建main目錄…

Java 字節碼操縱框架 -ASM

Java 字節碼操縱框架 -ASM 1.ASM 概述: ASM 是用于 Java 字節碼操縱的框架,可動態生成新類或增強現有類的功能。它既能直接產生二進制 class 文件,也能在類被加載到虛擬機之前動態改變類行為,通過讀取類文件信息來分析、修改類行為,甚至生成新類。許多流行框架如 cglib、…

kafka + flink +mysql 案例

假設你有兩個Kafka主題&#xff1a;user_activities_topic 和 product_views_topic&#xff0c;并且你希望將user_activities_topic中的數據寫入到user_activities表&#xff0c;而將product_views_topic中的數據寫入到product_views表。 maven <dependencies><!-- …

遠程登錄客戶端軟件 CTerm 發布了 v4.0.0

有時候我們需要遠程登錄到 Linux/Unix 服務器&#xff0c;這方面使用最廣泛的客戶端軟件是 PuTTY&#xff0c;不過它是全英文的&#xff0c;而且是單窗口的&#xff0c;有時候顯得不那么方便。 CTerm (Clever Terminal) 是一個 Windows 平臺下支持 Telnet 和 SSH 協議進行遠程…

從李佳琦團隊看新型用工:靈活就業如何重構組織架構?

2022年“雙11”期間&#xff0c;李佳琦直播間累計銷售額突破115億元&#xff08;來源&#xff1a;新腕數據《2022雙11直播電商戰報》&#xff09;&#xff0c;其背后團隊規模約400人&#xff0c;但全職員工僅占35%&#xff0c;其余65%為外包選品團隊、兼職客服、第三方MCN機構人…

微軟程序的打包格式MSIX

MSIX 微軟推出的MSIX格式是其為統一Windows應用程序打包和部署而設計的新一代安裝包格式&#xff0c;具有以下核心特點和進展&#xff1a; 1. 推出背景與時間線 MSIX最初于2018年在微軟Build大會上宣布&#xff0c;并在同年7月發布預覽版打包工具&#xff0c;10月正式版上線…

AFL++安裝

學習fuzzing也幾天了&#xff0c;今天記錄AFL的安裝及使用 一、實驗環境 虛擬機&#xff1a;ubuntu20.04 當然也可以uname -a去看自己的版本號 二、AFL安裝 1.先更新一下工具 sudo apt update2.安裝AFL必要的一些依賴&#xff0c;例如編譯工具&#xff08;如 build-essen…

【STM32】ADC功能-單通道多通道(學習筆記)

本章結合上一節內容復習更好理解【江協科技STM32】ADC數模轉換器-學習筆記-CSDN博客 一、ADC單通道 接線圖 ADC初始化 ①RCC開啟時鐘&#xff0c;包括ADC和GPIO的時鐘&#xff0c;另外ADCCLK的分頻器也要配置 ②配置GPIO,&#xff0c;把需要用的GPIO配置成模擬輸入模式&am…

基于YOLO11深度學習的運動品牌LOGO檢測與識別系統【python源碼+Pyqt5界面+數據集+訓練代碼】

《------往期經典推薦------》 一、AI應用軟件開發實戰專欄【鏈接】 項目名稱項目名稱1.【人臉識別與管理系統開發】2.【車牌識別與自動收費管理系統開發】3.【手勢識別系統開發】4.【人臉面部活體檢測系統開發】5.【圖片風格快速遷移軟件開發】6.【人臉表表情識別系統】7.【…

當前主流的大模型訓練與推理框架的全面匯總

以下是當前主流的大模型訓練與推理框架的全面匯總 以下是更新后包含 SGLang 的大模型訓練與推理框架列表&#xff0c;并對分類和示例進行了優化&#xff1a; 一、通用深度學習推理框架 TensorRT-LLM 特點&#xff1a;NVIDIA推出的針對Transformer類模型的優化框架&#xff0c;支…

Linux學習(八)(服務管理(檢查服務狀態,開始/停止服務,檢查服務日志,創建新服務))

服務管理 Linux 中的服務管理是指控制 Linux 在啟動和關閉計算機的過程中啟動和停止的服務&#xff08;或“守護程序”&#xff09;的系統。這些服務執行各種功能&#xff0c;并提供未附加到用戶界面的進程。 Linux 系統&#xff0c;尤其是系統管理員&#xff0c;通常需要管理…

ElasticSearch 分詞器介紹及測試:Standard(標準分詞器)、English(英文分詞器)、Chinese(中文分詞器)、IK(IK 分詞器)

ElasticSearch 分詞器介紹及測試&#xff1a;Standard&#xff08;標準分詞器&#xff09;、English&#xff08;英文分詞器&#xff09;、Chinese&#xff08;中文分詞器&#xff09;、IK&#xff08;IK 分詞器&#xff09; ElasticSearch 分詞器介紹及測試1. Standard Analyz…

【計算機網絡】確認家庭網絡是千兆/百兆帶寬并排查問題

要確認你的帶寬是千兆&#xff08;1000Mbps&#xff09;還是百兆&#xff08;100Mbps&#xff09;&#xff0c;可以通過以下方法逐步排查&#xff1a; 一、檢查物理設備 1. 查看路由器和光貓的網口 千兆網口&#xff1a;路由器或光貓的網口旁通常會標注 “10/100/1000M” 或 …