一、前言
來(lái)制作一個(gè)簡(jiǎn)易的 [Shell 命令]行解釋器。
首先這是與 Shell 的互動(dòng)::

用下圖的[時(shí)間軸]來(lái)表示事件的發(fā)生次序。其中時(shí)間從> > 左向右。shell 由標(biāo)識(shí)為 sh 的方塊代表,它隨著時(shí)間的流逝從左向右移動(dòng)。shell 從用戶讀入字符串 "ls"。shell 建立一個(gè)新的進(jìn)程,然后在那個(gè)進(jìn)程中運(yùn)行 ls 程序并等待那個(gè)進(jìn)程結(jié)束。

然后 shell 讀取新的一行輸入,建立一個(gè)新的進(jìn)程,在這個(gè)進(jìn)程中運(yùn)行程序 并等待這個(gè)進(jìn)程結(jié)束。所以要寫(xiě)一個(gè) shell,需要循環(huán)以下過(guò)程:
1. 獲取命令行
2. 解析命令行
3. 建立一個(gè)子進(jìn)程(fork)
4. 替換子進(jìn)程(execvp)
5. 父進(jìn)程等待子進(jìn)程退出(wait)
二、準(zhǔn)備工作
1.輸出提示符

這里的提示字符為用戶名 @主機(jī)名 當(dāng)前路徑# 直接打印出來(lái)作為提示所用
printf("用戶名@主機(jī)名當(dāng)前路徑#");
這里沒(méi)有 n,會(huì)有緩沖區(qū)的問(wèn)題,類似于我們之前所說(shuō)的進(jìn)度條所遇到的問(wèn)題,可以用 fflush(stdout) 刷新緩沖區(qū)。
2. 輸入和獲取命令
輸入
我們需要輸入一連串命令,其中可能出現(xiàn)空格,所以不能使用 gets 函數(shù),需要用到 fgets 函數(shù),同時(shí),可以定義一個(gè) lineCommand[NUM] 數(shù)組
#defineNUM1024 charlineCommand[NUM]; char*s=fgets(lineCommand,sizeof(lineCommand)-1,stdin); assert(s!=NULL);
但是打印的時(shí)候卻多換了一行,這是我們把 n 也讀取到了,直接進(jìn)行處理即可, 清除最后一個(gè) n
lineCommand[strlen(lineCommand)-1]=0;
可以通過(guò)打印看看效果和測(cè)試是否有 BUG
printf("test:%s
",lineCommand);

獲取
輸入之后,我們自然需要去進(jìn)行獲取,我們需要分割命令行,這個(gè)地方用 strtok。把字符串切割成若干個(gè)子串:
strtok: 第一次直接傳遞參數(shù),第二次則必須傳 NULL。且在最終 strtok 會(huì)返回 NULL。


3.shell 運(yùn)行原理
同時(shí),在理解一下 shell 的運(yùn)行原理:shell 內(nèi)部提取命令行做分析,然后調(diào)用 exec. shell 執(zhí)行命令必須通過(guò)創(chuàng)建子進(jìn)程,如果不創(chuàng)建子進(jìn)程會(huì)把我們所有的 shell 全部替換,所以執(zhí)行命令時(shí)一般磁盤(pán)上的程序必須創(chuàng)建子進(jìn)程。
4. 內(nèi)建命令
我們?cè)谶\(yùn)行自己寫(xiě)的 shell 的時(shí)候,發(fā)現(xiàn)輸入 cd … 輸入 cd path 等命令時(shí)發(fā)現(xiàn)路徑并沒(méi)有改變!

沒(méi)有發(fā)生改變是因?yàn)樽约簩?xiě)的 shell 執(zhí)行很多命令都要 fork() 創(chuàng)建子進(jìn)程,讓子進(jìn)程執(zhí)行的 cd,子進(jìn)程有自己的工作目錄,所以更改的子進(jìn)程的目錄,子進(jìn)程執(zhí)行完畢,繼續(xù)用的是父進(jìn)程,既 shell,并沒(méi)有影響父進(jìn)程,所以并沒(méi)有改變。
對(duì)于 cd, 我們可以采用內(nèi)建命令:不需要?jiǎng)?chuàng)建子進(jìn)程執(zhí)行,讓 shell 自己執(zhí)行命令,稱為內(nèi)建命令。本質(zhì)就是執(zhí)行系統(tǒng)接口,我們可以調(diào)用一個(gè)系統(tǒng)接口 chdir,可解決上述問(wèn)題:


5. 替換
采用 execvp 進(jìn)行替換進(jìn)程
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];//指針數(shù)組
intlastcode=0;
intlastsig=0;
intmain()
{
while(1)
{
//1.輸出提示符
printf("lj@VM-8-2-centos當(dāng)前路徑#");
fflush(stdout);
//2.獲取用戶輸入的命令,輸入的時(shí)候,用戶最后還輸入了
char*s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
assert(s!=NULL);
(void)s;//避免Linux認(rèn)為s變量未使用,導(dǎo)致警告
//清除最后一個(gè)
;例如: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";
}
//如果沒(méi)有子串了,strtok會(huì)返回NULL,即myargv[end]=NULL
while(myargv[i++]=strtok(NULL,""));
//如果是cd命令,不需要?jiǎng)?chuàng)建子進(jìn)程,讓shell自己執(zhí)行對(duì)應(yīng)的命令,本質(zhì)就是執(zhí)行系統(tǒng)接口
//像這種不需要讓我們的子進(jìn)程來(lái)執(zhí)行,而是讓shell自己執(zhí)行的命令—內(nèi)建命令
//其中echo是一個(gè)自建命令
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;
}
//利用條件編譯測(cè)試是否成功
#ifdefDEBUG
for(inti=0;myargv[i];++i)
{
printf("myargv[%d]:%s
",i,myargv[i]);
}
#endif
//執(zhí)行命令
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
+關(guān)注
關(guān)注
88文章
11770瀏覽量
219110 -
命令行
+關(guān)注
關(guān)注
0文章
83瀏覽量
10768 -
Shell
+關(guān)注
關(guān)注
1文章
375瀏覽量
25413 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
211瀏覽量
14544 -
解釋器
+關(guān)注
關(guān)注
0文章
103瀏覽量
6995
原文標(biāo)題:Linux 實(shí)現(xiàn)簡(jiǎn)易的 Shell 命令行解釋器
文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
在STM32實(shí)現(xiàn)命令行
Linux圖形界面的原理與構(gòu)成和Linux命令行和vi編輯器的使用手冊(cè)
Linux桌面系統(tǒng)初級(jí)教程之Shell命令行操作的資料概述
Linux 命令行教程好書(shū)推薦
mini shell命令行調(diào)試工具(單片機(jī)、c語(yǔ)言)
Linux實(shí)現(xiàn)簡(jiǎn)易的Shell命令行解釋器
評(píng)論