一、前言
來制作一個簡易的 [Shell 命令]行解釋器。
首先這是與 Shell 的互動::
用下圖的[時間軸]來表示事件的發生次序。其中時間從> > 左向右。shell 由標識為 sh 的方塊代表,它隨著時間的流逝從左向右移動。shell 從用戶讀入字符串 "ls"。shell 建立一個新的進程,然后在那個進程中運行 ls 程序并等待那個進程結束。
然后 shell 讀取新的一行輸入,建立一個新的進程,在這個進程中運行程序 并等待這個進程結束。所以要寫一個 shell,需要循環以下過程:
1. 獲取命令行
2. 解析命令行
3. 建立一個子進程(fork)
4. 替換子進程(execvp)
5. 父進程等待子進程退出(wait)
二、準備工作
1.輸出提示符
這里的提示字符為用戶名 @主機名 當前路徑# 直接打印出來作為提示所用
printf("用戶名@主機名當前路徑#");
這里沒有 n,會有緩沖區的問題,類似于我們之前所說的進度條所遇到的問題,可以用 fflush(stdout) 刷新緩沖區。
2. 輸入和獲取命令
輸入
我們需要輸入一連串命令,其中可能出現空格,所以不能使用 gets 函數,需要用到 fgets 函數,同時,可以定義一個 lineCommand[NUM] 數組
#defineNUM1024 charlineCommand[NUM]; char*s=fgets(lineCommand,sizeof(lineCommand)-1,stdin); assert(s!=NULL);
但是打印的時候卻多換了一行,這是我們把 n 也讀取到了,直接進行處理即可, 清除最后一個 n
lineCommand[strlen(lineCommand)-1]=0;
可以通過打印看看效果和測試是否有 BUG
printf("test:%s ",lineCommand);
獲取
輸入之后,我們自然需要去進行獲取,我們需要分割命令行,這個地方用 strtok。把字符串切割成若干個子串:
strtok: 第一次直接傳遞參數,第二次則必須傳 NULL。且在最終 strtok 會返回 NULL。
3.shell 運行原理
同時,在理解一下 shell 的運行原理:shell 內部提取命令行做分析,然后調用 exec. shell 執行命令必須通過創建子進程,如果不創建子進程會把我們所有的 shell 全部替換,所以執行命令時一般磁盤上的程序必須創建子進程。
4. 內建命令
我們在運行自己寫的 shell 的時候,發現輸入 cd … 輸入 cd path 等命令時發現路徑并沒有改變!
沒有發生改變是因為自己寫的 shell 執行很多命令都要 fork() 創建子進程,讓子進程執行的 cd,子進程有自己的工作目錄,所以更改的子進程的目錄,子進程執行完畢,繼續用的是父進程,既 shell,并沒有影響父進程,所以并沒有改變。
對于 cd, 我們可以采用內建命令:不需要創建子進程執行,讓 shell 自己執行命令,稱為內建命令。本質就是執行系統接口,我們可以調用一個系統接口 chdir,可解決上述問題:
5. 替換
采用 execvp 進行替換進程
pid_tid=fork(); assert(id!=-1); if(id==0) { execvp(myargv[0],myargv); exit(1); }
三、整體代碼
#include #include #include #include #include #include #include #defineNUM1024 #defineOPT_NUM64 charlineCommand[NUM]; char*myargv[OPT_NUM];//指針數組 intlastcode=0; intlastsig=0; intmain() { while(1) { //1.輸出提示符 printf("lj@VM-8-2-centos當前路徑#"); fflush(stdout); //2.獲取用戶輸入的命令,輸入的時候,用戶最后還輸入了 char*s=fgets(lineCommand,sizeof(lineCommand)-1,stdin); assert(s!=NULL); (void)s;//避免Linux認為s變量未使用,導致警告 //清除最后一個 ;例如:abcd lineCommand[strlen(lineCommand)-1]=0; //printf("test:%s ",lineCommand); //"ls-a-l-i"-->字符串分割-->"ls""-a""-l""-i" myargv[0]=strtok(lineCommand,""); inti=1; if(myargv[0]!=NULL&&(strcmp(myargv[0],"ls")==0)) { myargv[i++]=(char*)"--color=auto"; } //如果沒有子串了,strtok會返回NULL,即myargv[end]=NULL while(myargv[i++]=strtok(NULL,"")); //如果是cd命令,不需要創建子進程,讓shell自己執行對應的命令,本質就是執行系統接口 //像這種不需要讓我們的子進程來執行,而是讓shell自己執行的命令—內建命令 //其中echo是一個自建命令 if(myargv[0]!=NULL&&(strcmp(myargv[0],"cd")==0)) { if(myargv[1]!=NULL)chdir(myargv[1]); continue; } if(myargv[0]!=NULL&&myargv[1]!=NULL&&(strcmp(myargv[0],"echo")==0)) { if(strcmp(myargv[1],"$?")==0) { printf("%d,%d ",lastcode,lastsig); } else { printf("%s ",myargv[i]); } continue; } //利用條件編譯測試是否成功 #ifdefDEBUG for(inti=0;myargv[i];++i) { printf("myargv[%d]:%s ",i,myargv[i]); } #endif //執行命令 pid_tid=fork(); assert(id!=-1); if(id==0) { execvp(myargv[0],myargv); exit(1); } intstatus=0; pid_tret=waitpid(id,&status,0); assert(ret>0); (void)ret; lastcode=(status>>8)&0xFF; lastsig=status&0x7F; } return0; }
審核編輯:湯梓紅
-
Linux
+關注
關注
87文章
11319瀏覽量
209830 -
命令行
+關注
關注
0文章
78瀏覽量
10405 -
Shell
+關注
關注
1文章
366瀏覽量
23412 -
進程
+關注
關注
0文章
203瀏覽量
13965 -
解釋器
+關注
關注
0文章
103瀏覽量
6542
原文標題:Linux 實現簡易的 Shell 命令行解釋器
文章出處:【微信號:良許Linux,微信公眾號:良許Linux】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論