0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux實(shí)現(xiàn)簡(jiǎn)易的Shell命令行解釋器

dyquk4xk2p3d ? 來(lái)源:入門(mén)小站 ? 作者:入門(mén)小站 ? 2023-03-30 10:00 ? 次閱讀

一、前言

來(lái)制作一個(gè)簡(jiǎn)易的 [Shell 命令]行解釋器。

首先這是與 Shell 的互動(dòng)::

ae1c7a54-ce8a-11ed-bfe3-dac502259ad0.png

用下圖的[時(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é)束。

ae3edf4a-ce8a-11ed-bfe3-dac502259ad0.png

然后 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.輸出提示符

ae709f44-ce8a-11ed-bfe3-dac502259ad0.png

這里的提示字符為用戶名 @主機(jī)名 當(dāng)前路徑# 直接打印出來(lái)作為提示所用

printf("用戶名@主機(jī)名當(dāng)前路徑#");

這里沒(méi)有 n,會(huì)有緩沖區(qū)的問(wèn)題,類(lèi)似于我們之前所說(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);
ae85b9c4-ce8a-11ed-bfe3-dac502259ad0.png

獲取

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

ae99dc06-ce8a-11ed-bfe3-dac502259ad0.pngaec2e524-ce8a-11ed-bfe3-dac502259ad0.png

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)有改變!

aee97ca2-ce8a-11ed-bfe3-dac502259ad0.png

沒(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í)行命令,稱(chēng)為內(nèi)建命令。本質(zhì)就是執(zhí)行系統(tǒng)接口,我們可以調(diào)用一個(gè)系統(tǒng)接口 chdir,可解決上述問(wèn)題:

aefa5928-ce8a-11ed-bfe3-dac502259ad0.png
af185b26-ce8a-11ed-bfe3-dac502259ad0.png

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;
}
af335782-ce8a-11ed-bfe3-dac502259ad0.png

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11314

    瀏覽量

    209807
  • 命令行
    +關(guān)注

    關(guān)注

    0

    文章

    77

    瀏覽量

    10404
  • Shell
    +關(guān)注

    關(guān)注

    1

    文章

    366

    瀏覽量

    23411
  • 進(jìn)程
    +關(guān)注

    關(guān)注

    0

    文章

    203

    瀏覽量

    13964
  • 解釋器
    +關(guān)注

    關(guān)注

    0

    文章

    103

    瀏覽量

    6542

原文標(biāo)題:Linux 實(shí)現(xiàn)簡(jiǎn)易的 Shell 命令行解釋器

文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    在STM32實(shí)現(xiàn)命令行

    工作中的開(kāi)發(fā)環(huán)境都是基于linux命令行交互,作為命令行的重度使用者,玩單片機(jī)也要使用命令行工具,百度了一些命令行工具,有幾個(gè)不錯(cuò)的開(kāi)源 c
    發(fā)表于 12-09 11:32 ?2453次閱讀

    Linux圖形界面的原理與構(gòu)成和Linux命令行和vi編輯的使用手冊(cè)

    本文檔的主要內(nèi)容詳細(xì)介紹的是Linux基礎(chǔ)教案主要內(nèi)容是:(1) 了解Linux圖形界面的原理與構(gòu)成;(2)掌握Linux命令行操作,包括:命令行
    發(fā)表于 10-16 14:49 ?6次下載
    <b class='flag-5'>Linux</b>圖形界面的原理與構(gòu)成和<b class='flag-5'>Linux</b><b class='flag-5'>命令行</b>和vi編輯<b class='flag-5'>器</b>的使用手冊(cè)

    Linux桌面系統(tǒng)初級(jí)教程之Shell命令行操作的資料概述

    Linux shell指的是一種程序,有了它,用戶就能通過(guò)鍵盤(pán)輸入指令來(lái)操作計(jì)算機(jī)了。Shell會(huì)執(zhí)行用戶輸入的命令,并且在顯示上顯示執(zhí)行
    發(fā)表于 11-09 17:42 ?18次下載
    <b class='flag-5'>Linux</b>桌面系統(tǒng)初級(jí)教程之<b class='flag-5'>Shell</b><b class='flag-5'>命令行</b>操作的資料概述

    Linux 命令行教程好書(shū)推薦

    今天跟大家推薦個(gè) Linux 命令行教程:《The Linux Command Line》,中文譯名:《Linux 命令行大全》。 該書(shū)作者
    的頭像 發(fā)表于 02-14 09:25 ?1485次閱讀

    mini shell命令行調(diào)試工具(單片機(jī)、c語(yǔ)言)

    @mini shell命令行調(diào)試工具介紹Mini shell 命令行調(diào)試工具(單片機(jī)、c語(yǔ)言)Mini shell是一個(gè)特別適合低內(nèi)存的單片
    發(fā)表于 11-29 10:21 ?10次下載
    mini <b class='flag-5'>shell</b><b class='flag-5'>命令行</b>調(diào)試工具(單片機(jī)、c語(yǔ)言)

    Shell命令行解釋簡(jiǎn)介

    Shell 是一個(gè)命令行解釋,Shell 為用戶提供了與設(shè)備進(jìn)行命令行交互的方式,用戶通過(guò)串口
    的頭像 發(fā)表于 08-19 17:20 ?3241次閱讀

    Linux命令行shell腳本編寫(xiě)

    Linux命令行shell腳本編寫(xiě)
    發(fā)表于 01-11 16:50 ?4次下載

    如何在Linux命令行中運(yùn)行Python腳本

    Python 是一種高級(jí)編程語(yǔ)言,被廣泛應(yīng)用于數(shù)據(jù)科學(xué)、機(jī)器學(xué)習(xí)、Web 開(kāi)發(fā)等領(lǐng)域。在 Linux 操作系統(tǒng)中,Python 是一個(gè)默認(rèn)安裝的解釋,用戶可以通過(guò)命令行界面(CLI)
    的頭像 發(fā)表于 05-12 14:49 ?1696次閱讀

    單片機(jī)上如何做shell命令行交互

    做個(gè)shell命令行交互?答案當(dāng)然是可以的,在網(wǎng)上類(lèi)似的文章和代碼一搜一籮筐, 基本原理: 監(jiān)測(cè)用戶的輸入,然后到一個(gè)命令查找表里過(guò)濾是否可以找到該命令,如果可以則調(diào)用對(duì)應(yīng)的處理函數(shù),
    的頭像 發(fā)表于 11-01 15:16 ?1421次閱讀

    shell命令linux命令一樣嗎

    Shell命令Linux命令并不完全一樣。 首先,Shell是一種命令行
    的頭像 發(fā)表于 11-08 10:51 ?2901次閱讀

    linux命令shell編程有什么聯(lián)系

    Linux命令Shell編程之間存在密切的聯(lián)系。 首先,ShellLinux命令行下的
    的頭像 發(fā)表于 11-08 10:53 ?943次閱讀

    linux命令行shell編程實(shí)戰(zhàn)

    Linux命令行Shell編程實(shí)戰(zhàn)主要涉及以下內(nèi)容: Linux命令行基礎(chǔ):學(xué)習(xí)Linux
    的頭像 發(fā)表于 11-08 10:57 ?792次閱讀

    linux虛擬機(jī)怎么調(diào)出命令行

    Linux虛擬機(jī)中調(diào)出命令行界面,可以通過(guò)以下步驟實(shí)現(xiàn): 打開(kāi)虛擬機(jī),進(jìn)入到Linux系統(tǒng)。 在桌面或應(yīng)用菜單中找到終端或命令行圖標(biāo),點(diǎn)擊
    的頭像 發(fā)表于 11-08 11:28 ?3146次閱讀

    linux切換到命令行模式

    Linux中,可以通過(guò)以下步驟切換到命令行模式: 打開(kāi)終端。可以在應(yīng)用菜單中找到終端或命令行終端。 在終端中輸入命令“exit”或“l(fā)ogout”,然后按回車(chē)鍵。 系統(tǒng)會(huì)提示您輸入管
    的頭像 發(fā)表于 11-13 16:47 ?1803次閱讀

    linux命令行運(yùn)行步驟

    運(yùn)行Linux命令行涉及以下步驟: 打開(kāi)終端 在Linux系統(tǒng)中,打開(kāi)命令行界面的方式有多種,最常見(jiàn)的是打開(kāi)終端應(yīng)用程序。可以在應(yīng)用程序菜單中找到終端,點(diǎn)擊打開(kāi)。 熟悉
    的頭像 發(fā)表于 11-17 10:18 ?810次閱讀