回顧一下,我們前面學(xué)習(xí)了進程創(chuàng)建,進程終止,進程等待,進程替換,通過這些內(nèi)容我們可以來進行實現(xiàn)簡單的shell命令行解釋器?。?!下面我們直接來看一看如何去實現(xiàn)shell命令行解釋器:
總體分為(整體需要循環(huán)哦):
1.輸出提示符
2.輸入和獲取命令
3.fork創(chuàng)建子進程
4.內(nèi)建命令
- 輸出提示符
這里的提示字符為用戶名@主機名 當(dāng)前路徑# 直接打印出來作為提示所用,也可以自己設(shè)置成其他的,問題不大
printf("用戶名@主機名 當(dāng)前路徑# ");
同時,這里并沒有\(zhòng)n,會有緩沖區(qū)的問題,類似于我們之前所說的進度條所遇到的問題,在這個地方哦可以用fflush(stdout)刷新緩沖區(qū)
- 輸入和獲取命令
輸入
我們需要獲取一行的內(nèi)容,利用fgets函數(shù)獲取,同時,可以定義一個lineCommand[NUM]數(shù)組
char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin); assert(s != NULL);
但是打印的時候卻多換了一行,這是我們把\n也讀取到了,直接進行處理即可,清除最后一個\n
lineCommand[strlen(lineCommand)-1] = 0;
獲取
輸入之后,我們自然需要去進行獲取,我們需要分割命令行,這個地方用strtok。把字符串切割成若干個子串:
strtok:第一次直接傳遞參數(shù),第二次則必須傳NULL。且在最終strtok會返回NULL。
- fork創(chuàng)建進程
利用fork創(chuàng)建子進程,同時父進程需要等待子進程退出返回結(jié)果
另外我們還需要選擇替換函數(shù)execvp:首先替換函數(shù)需要先帶上v,可將所有的執(zhí)行參數(shù)放入數(shù)組中統(tǒng)一傳遞,其次還要選擇帶上p,我們輸入的只有程序命令,帶上p會自動在環(huán)境變量中尋找
至此,基本的框架我們已經(jīng)搞定了。
- shell運行原理
同時,在理解一下shell的運行原理:shell內(nèi)部提取命令行做分析,然后調(diào)用exec. shell執(zhí)行命令必須通過創(chuàng)建子進程,如果不創(chuàng)建子進程會把我們所有的shell全部替換,所以執(zhí)行命令時一般磁盤上的程序必須創(chuàng)建子進程
- 內(nèi)建命令
我們在運行自己寫的shell的時候,發(fā)現(xiàn)輸入cd …輸入cd path等命令時發(fā)現(xiàn)路徑并沒有改變!
沒有發(fā)生改變是因為自己寫的shell執(zhí)行很多命令都要fork()創(chuàng)建子進程,讓子進程執(zhí)行的cd,子進程有自己的工作目錄,所以更改的子進程的目錄,子進程執(zhí)行完畢,繼續(xù)用的是父進程,既shell,并沒有影響父進程,所以并沒有改變。
對于cd,我們可以采用內(nèi)建命令:不需要創(chuàng)建子進程執(zhí)行,讓shell自己執(zhí)行命令,稱為內(nèi)建命令。本質(zhì)就是執(zhí)行系統(tǒng)接口,我們可以調(diào)用一個系統(tǒng)接口chdir,可解決上述問題:
簡易shell——代碼實現(xiàn)
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#define NUM 1024
#define OPT_NUM 64
char lineCommand[NUM];
char *myargv[OPT_NUM];
int lastCode = 0;
int lastSig = 0;
int main()
{
while(1)
{
//輸出提示符
printf("用戶名@主機名 當(dāng)前路徑#");
fflush(stdout);
//獲取輸入
char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
assert(s != NULL);
(void) s;
lineCommand[strlen(lineCommand)-1] = 0;
// printf("test:%s\n",lineCommand);
//ls -a -l -i 字符串切割
myargv[0] = strtok(lineCommand," ");
int i = 1;
if(myargv[0]!= NULL&& strcmp(myargv[0],"ls") == 0)
{
myargv[i++] =(char*)"--color=auto";
}
//如果沒有子串,strtok會返回NULL
while(myargv[i++] = strtok(NULL," "));
//如果是cd命令, 不需要創(chuàng)建子進程,讓shell自己執(zhí)行對應(yīng)的命令,本質(zhì)就是執(zhí)行系統(tǒng)接口
//像這種不需要我們的子進程來執(zhí)行,而是讓shell自己執(zhí)行的命令 --內(nèi)建 內(nèi)置命令
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\n",lastCode,lastSig);
}
else
{
printf("%s\n",myargv[1]);
}
continue;
}
//利用條件編譯測試代碼是否成功
#ifdef DEBUG
for(int i = 0;myargv[i];i++)
{
printf("myargv[%d]:%s\n",i,myargv[i]);
}
#endif
//執(zhí)行命令
pid_t id = fork();
assert(id!=-1);
if(id == 0)
{
execvp(myargv[0],myargv);
exit(1);
}
int status = 0;
pid_t ret = waitpid(id,&status,0);
assert(ret > 0);
(void)ret;
lastCode = ((status>>8)&0xFF);
lastSig = (status & 0X7F);
}
return 0;
}
文章來源:http://www.zghlxwxcb.cn/news/detail-789809.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-789809.html
到了這里,關(guān)于【Linux】shell命令行簡單解釋器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!