欧美麻豆久久久久久中文_成年免费观看_男人天堂亚洲成人_中国一级片_动漫黄网站免费永久在线观看_国产精品自产av一区二区三区

中培偉業(yè)IT資訊頻道
您現(xiàn)在的位置:首頁(yè) > IT資訊 > IT運(yùn)維 > 如何構(gòu)建一個(gè)Linux Shell(五)

如何構(gòu)建一個(gè)Linux Shell(五)

2020-08-31 18:47:59 | 來源:中培企業(yè)IT培訓(xùn)網(wǎng)

之前為家介紹了關(guān)于如何構(gòu)建一個(gè)Linux Shell的一、二、三、四。今天這里為大家介紹的是關(guān)于如何構(gòu)建Linux Shell的教程的第五部分。正如我們?cè)谇懊娴牟糠种兴吹降模粋€(gè)簡(jiǎn)單的命令由一個(gè)或多個(gè)參數(shù)組成。第一個(gè)單詞包含我們要執(zhí)行的命令的名稱,其余單詞包含命令的參數(shù)。在外殼程序執(zhí)行命令之前,它將對(duì)命令字執(zhí)行字?jǐn)U展。

單詞擴(kuò)展是shell提取命令字,檢查它是否包含變量名,路徑名,命令和算術(shù)表達(dá)式,然后用其值替換每個(gè)名稱/命令/表達(dá)式的過程。

通常長(zhǎng)于原始單詞的結(jié)果單詞可以在稱為字段拆分的過程中分解為一個(gè)或多個(gè)子單詞。

在這一部分中,我們將實(shí)現(xiàn)POSIX定義的7個(gè)詞擴(kuò)展,它們是:波浪號(hào)擴(kuò)展,參數(shù)擴(kuò)展,算術(shù)擴(kuò)展,命令替換,字段拆分,路徑名擴(kuò)展和引號(hào)刪除。還有其他單詞擴(kuò)展,例如大括號(hào)擴(kuò)展和流程替換,這些沒有由POSIX定義,因此我們將不在這里討論。完成本課程后,如果您通過實(shí)現(xiàn)非POSIX單詞擴(kuò)展來擴(kuò)展Shell,將是一個(gè)很好的練習(xí)。

  單詞擴(kuò)展過程

當(dāng)外殼程序執(zhí)行單詞擴(kuò)展時(shí),它將檢查命令行中的每個(gè)單詞以查看其是否包含可能的單詞擴(kuò)展。擴(kuò)展可以出現(xiàn)在單詞的任何位置:在單詞的開頭,中間或結(jié)尾。擴(kuò)展可能還包括整個(gè)單詞。

單詞擴(kuò)展之前有一個(gè)$標(biāo)志。后面的字符$符號(hào)指示外殼要執(zhí)行的擴(kuò)展類型。這些字符由外殼解釋如下:

一個(gè)或多個(gè)數(shù)字,指示位置參數(shù)的變量擴(kuò)展。

之一@,*,#,?,-,$,!,要么0,它指示特殊參數(shù)的變量擴(kuò)展。

一個(gè)或多個(gè)字母數(shù)字字符和/或下劃線,以字母或下劃線開頭,表示外殼變量名稱。

括號(hào)內(nèi)的變量名{和}。

算術(shù)展開,被(和)。

命令替換,由((和))。

Shell首先執(zhí)行波浪號(hào)擴(kuò)展,參數(shù)擴(kuò)展,命令替換和算術(shù)擴(kuò)展,然后進(jìn)行字段拆分和路徑名擴(kuò)展。最后,shell從已擴(kuò)展單詞中刪除了已成為原始單詞一部分的所有引號(hào)字符。

  使用文字

當(dāng)外殼程序執(zhí)行單詞擴(kuò)展時(shí),該過程可能會(huì)導(dǎo)致零個(gè),一個(gè)或多個(gè)單詞。

structword_s

{

char*data;

intlen;

structword_s*next;

};

  該結(jié)構(gòu)包含以下字段:

data=>表示此單詞的字符串。

len=>的長(zhǎng)度data領(lǐng)域。

next=>指向下一個(gè)單詞的指針,或者NULL如果這是最后一個(gè)單詞。

  當(dāng)然,我們需要一些函數(shù)來分配和釋放我們的structword_s結(jié)構(gòu)。為此,我們將使用以下功能:

structword_s*make_word(char*str);

voidfree_all_words(structword_s*first);

第一個(gè)函數(shù)為該結(jié)構(gòu)分配內(nèi)存,創(chuàng)建單詞字符串的副本,然后返回新分配的單詞。第二個(gè)功能釋放單詞結(jié)構(gòu)列表使用的內(nèi)存。您可以在我們的網(wǎng)站中閱讀功能代碼wordexp.c源文件。

  定義一些助手功能

如前所述,詞擴(kuò)展是一個(gè)復(fù)雜的過程,為此,我們需要定義許多不同的功能。在深入研究單詞擴(kuò)展的細(xì)節(jié)之前,讓我們先定義一些輔助函數(shù)。

以下列表顯示了我們的輔助函數(shù)的函數(shù)原型,所有這些我們將在wordexp.c源文件:

char*wordlist_to_str(structword_s*word);

voiddelete_char_at(char*str,size_tindex);

intis_name(char*str);

size_tfind_closing_quote(char*data);

size_tfind_closing_brace(char*data);

char*substitute_str(char*s1,char*s2,size_tstart,size_tend);

intsubstitute_word(char**pstart,char**p,size_tlen,char*(func)(char*),intadd_quotes);

以下是這些函數(shù)的功能細(xì)目:

wordlist_to_str()=>將擴(kuò)展單詞的鏈接列表轉(zhuǎn)換為單個(gè)字符串。

delete_char_at()=>從字符串中刪除給定索引處的字符。

is_name()=>檢查字符串是否表示有效的變量名。

find_closing_quote()=>當(dāng)單詞擴(kuò)展包含開頭的引號(hào)",',要么`,我們需要找到匹配的用引號(hào)引起來的字符,該字符將引號(hào)引起來的字符串括起來。此函數(shù)返回單詞中結(jié)束引號(hào)字符的從零開始的索引。

find_closing_brace()=>與上面類似,不同之處在于它找到匹配的右括號(hào)。也就是說,如果左括號(hào)是{,(,要么[,此函數(shù)返回匹配項(xiàng)的從零開始的索引},),要么]字符。查找引號(hào)對(duì)對(duì)于處理參數(shù)擴(kuò)展,算術(shù)擴(kuò)展和命令替換很重要。

substitute_str()=>替換的子字符串s1,從位置上的角色開始start到那個(gè)位置end,s2串。當(dāng)單詞擴(kuò)展是較長(zhǎng)單詞的一部分例如,${PATH}/ls,在這種情況下,我們只需要擴(kuò)展${PATH},然后附加/ls到擴(kuò)展字符串的末尾。

substitute_word()=>一個(gè)幫助程序函數(shù),它調(diào)用其他單詞擴(kuò)展功能,我們將在以下各節(jié)中對(duì)其進(jìn)行定義。

另外,我們將定義一些函數(shù)來幫助我們處理字符串。我們將在strings.c源文件:

char*strchr_any(char*string,char*chars);

char*quote_val(char*val,intadd_quotes);

intcheck_buffer_bounds(int*count,int*len,char***buf);

voidfree_buffer(intlen,char**buf);

  這些功能的作用如下:

strchr_any()=>類似于strchr(),除了它會(huì)在給定的字符串中搜索任何給定的字符。

quote_val()=>執(zhí)行與引號(hào)刪除相反的操作,即將字符串轉(zhuǎn)換為帶引號(hào)的字符串。

的check_buffer_bounds()和free_buffer()函數(shù)將使我們的后端執(zhí)行器支持可變數(shù)量的命令參數(shù),而不是我們?cè)诘诙糠种性O(shè)置的硬限制255。

現(xiàn)在,讓我們編寫函數(shù)來處理每種類型的單詞擴(kuò)展。

  波浪號(hào)擴(kuò)展

在波浪符號(hào)擴(kuò)展期間,外殼程序?qū)⒉ɡ朔?hào)字符替換為用戶主目錄的路徑名。例如,~和~/波浪線擴(kuò)展到當(dāng)前用戶的主目錄,而~john被波浪擴(kuò)展到用戶John的主目錄,依此類推。除后面的所有字符之外,代字號(hào)字符被稱為代字號(hào)前綴

要執(zhí)行波浪線擴(kuò)展,我們將定義tilde_expand()函數(shù),具有以下原型:

char*tilde_expand(char*s);

該函數(shù)接受一個(gè)參數(shù):我們要擴(kuò)展的代字號(hào)前綴。如果擴(kuò)展成功,該函數(shù)返回一個(gè)的malloc表示波浪線擴(kuò)展前綴“d字符串。否則返回NULL。下面是該函數(shù)為擴(kuò)展代字號(hào)前綴所做的工作的快速分解:

如果前綴是~,獲得$HOME外殼變量。如果$HOME被定義而不是NULL,返回其值。否則,通過調(diào)用獲取當(dāng)前的用戶ID(UID)getuid(),然后將UID傳遞給getpwuid()獲取與當(dāng)前用戶相對(duì)應(yīng)的密碼數(shù)據(jù)庫(kù)條目。的pw_dir密碼數(shù)據(jù)庫(kù)條目的“字段”包含函數(shù)返回的主目錄的路徑名。

如果前綴包含其他字符除了前導(dǎo)字符~,我們將這些字母作為要獲取其主目錄的用戶的名稱。我們稱之為getpwnam(),將其傳遞給用戶名,然后返回pw_dir領(lǐng)域。

如果我們無法檢索主目錄,則返回NULL。否則,我們將返回主目錄路徑的malloc副本。

  參數(shù)擴(kuò)展

在參數(shù)擴(kuò)展中,外殼程序用變量的值替換外殼程序變量的名稱。參數(shù)擴(kuò)展使外殼程序可以執(zhí)行諸如echo$PATH。在此示例中,外殼程序?qū)?PATH變量,將其替換為實(shí)際的可執(zhí)行路徑。

為了向shell發(fā)出我們要擴(kuò)展shell變量的信號(hào),我們?cè)谧兞棵Q前添加一個(gè)$標(biāo)志。也就是說,擴(kuò)大PATH,USER和SHELL變量,我們需要傳遞$PATH,$USER和$SHELL將單詞分別傳遞給shell或者,我們可以將這些變量擴(kuò)展傳遞給shellshell,如下所示:${PATH},${USER}和${SHELL}。Shell變量名稱可以包含字母,數(shù)字和下劃線的任意組合。名稱可以包含大寫字母或小寫字母,盡管按照慣例,大寫名稱保留用于標(biāo)準(zhǔn)Shell變量。

我們可以使用參數(shù)擴(kuò)展修飾符來控制外殼如何執(zhí)行參數(shù)擴(kuò)展,該修飾符告訴外殼我們要擴(kuò)展值的哪一部分,以及在沒有給定名稱的外殼變量的情況下該怎么做。下表總結(jié)了參數(shù)擴(kuò)展修飾符由POSIX定義的修飾符在“描述”列中由POSIX單詞標(biāo)記。大多數(shù)外殼程序都支持其他修飾符,我們將不在此處討論。有關(guān)非POSIX修飾符的更多信息,請(qǐng)參見您的Shell的手冊(cè)頁(yè)。

要執(zhí)行參數(shù)擴(kuò)展,我們將定義var_expand()函數(shù),具有以下原型:

char*var_expand(char*orig_var_name);

該函數(shù)接受一個(gè)參數(shù):我們要擴(kuò)展的參數(shù)。如果擴(kuò)展成功,該函數(shù)將返回一個(gè)包含擴(kuò)展值的malloc'd字符串。否則返回NULL。下面是該函數(shù)為擴(kuò)展變量名以獲取其值而執(zhí)行的操作的快速細(xì)分:

如果變量名用大括號(hào)括起來,請(qǐng)刪除大括號(hào),因?yàn)樗鼈儾皇亲兞棵旧淼囊徊糠帧H绻Q以#,我們需要獲取變量名稱的長(zhǎng)度。

如果變量名稱包含冒號(hào),我們將使用它來將名稱與單詞或模式分開。單詞或圖案的使用如上表所示。獲取具有給定變量名稱的符號(hào)表?xiàng)l目。獲取符號(hào)表?xiàng)l目的值。

如果該值為空或?yàn)榭眨?qǐng)使用擴(kuò)展中提供的替代詞。

如果該值不為空,則將該值用作擴(kuò)展結(jié)果。要使外殼執(zhí)行模式匹配${parameter#word}和${parameter%word}擴(kuò)展,我們需要兩個(gè)幫助函數(shù):match_suffix()和match_prefix()。我們不會(huì)在這里討論這些功能,但是您可以從此鏈接中閱讀它們的代碼。

如果擴(kuò)展修飾符為${parameter:=word},我們需要將符號(hào)表?xiàng)l目的值設(shè)置為剛剛擴(kuò)展的值。

如果擴(kuò)展以#,獲取擴(kuò)展值的長(zhǎng)度并將其用作最終結(jié)果。返回?cái)U(kuò)展值的malloc'd副本或其長(zhǎng)度,視情況而定。

  命令替換

在命令替換中,shell派生一個(gè)進(jìn)程來運(yùn)行命令,然后用命令的輸出替換命令替換擴(kuò)展。例如,在以下循環(huán)中:

foriin$(ls);doecho$i;done

外殼分叉一個(gè)過程,其中l(wèi)s命令運(yùn)行。該命令的輸出是當(dāng)前目錄中的文件列表。Shell獲取該輸出,將其拆分為單詞列表,然后一次將這些單詞提供給循環(huán)。在循環(huán)的每次迭代中,變量$i被分配了列表中下一個(gè)文件的名稱。

此名稱將傳遞給echo命令,該命令在單獨(dú)的行上輸出名稱。

命令替換可以寫成$(command),要么`command`。要執(zhí)行命令替換,我們將定義command_substitute()函數(shù),具有以下原型:

char*command_substitute(char*orig_cmd);

該函數(shù)接受一個(gè)參數(shù):我們要執(zhí)行的命令。如果擴(kuò)展成功,則該函數(shù)將返回一個(gè)malloc'd字符串,表示命令的輸出。如果擴(kuò)展失敗,或者命令沒有輸出任何內(nèi)容,則函數(shù)返回NULL。

下面是該函數(shù)為擴(kuò)展命令替換而執(zhí)行的操作的快速細(xì)分:

根據(jù)使用的格式,我們首先刪除$()或反引號(hào)``。這給我們留下了我們需要執(zhí)行的命令。

呼叫popen()創(chuàng)建一個(gè)管道。我們將要執(zhí)行的命令傳遞給popen(),我們得到一個(gè)指向FILE流,我們將從中讀取命令的輸出。

呼叫fread()從管道讀取命令的輸出。將讀取的字符串存儲(chǔ)在緩沖區(qū)中。

刪除所有尾隨換行符。

關(guān)閉管道,并使用命令輸出返回緩沖區(qū)。

  算術(shù)擴(kuò)展

使用算術(shù)擴(kuò)展,我們可以讓外殼執(zhí)行不同的算術(shù)運(yùn)算,并將結(jié)果用于執(zhí)行其他命令。盡管POSIX要求外殼程序僅支持帶符號(hào)的長(zhǎng)整數(shù)算法,但許多外殼程序都支持浮點(diǎn)算法。

此外,盡管大多數(shù)外殼程序都不需要外殼程序來支持任何數(shù)學(xué)函數(shù)。對(duì)于簡(jiǎn)單的shell,我們將僅支持帶符號(hào)的長(zhǎng)整數(shù)算法,而沒有數(shù)學(xué)函數(shù)支持。

算術(shù)擴(kuò)展寫為$((expression))。

要執(zhí)行擴(kuò)展,我們將定義arithm_expand()函數(shù),具有以下原型:

char*arithm_expand(char*expr);

的arithm_expand()函數(shù)接收包含算術(shù)表達(dá)式的字符串,執(zhí)行必要的計(jì)算,然后以malloc'd字符串的形式返回結(jié)果。該函數(shù)及其相關(guān)的幫助器函數(shù)既復(fù)雜又冗長(zhǎng),但這是主要亮點(diǎn):

算術(shù)表達(dá)式轉(zhuǎn)換為反向波蘭表示法,更易于分析和計(jì)算。RPN由一系列算術(shù)運(yùn)算組成,其中運(yùn)算符遵循其操作數(shù)。例如,RPNx-y是xy-,以及3+4×(2?1)是3421?×+。

在轉(zhuǎn)換過程中,算術(shù)運(yùn)算符被壓入一個(gè)運(yùn)算符堆棧,我們將從中彈出每個(gè)運(yùn)算符并稍后執(zhí)行其運(yùn)算。同樣,操作數(shù)被添加到它們自己的堆棧中。

一次將操作員從堆棧中彈出,然后對(duì)操作員進(jìn)行檢查。根據(jù)運(yùn)算符的類型,一個(gè)或兩個(gè)操作數(shù)從堆棧中彈出。控制此過程的規(guī)則是調(diào)車場(chǎng)算法的規(guī)則,您可以在此處閱讀。

結(jié)果將轉(zhuǎn)換為字符串,然后將其返回給調(diào)用方。

  場(chǎng)分裂

在字段拆分期間,shell會(huì)獲取參數(shù)擴(kuò)展,命令替換和算術(shù)擴(kuò)展的結(jié)果,并將它們拆分為一個(gè)或多個(gè)部分,我們將其稱為字段。該過程取決于$IFS外殼變量。IFS是一個(gè)歷史術(shù)語(yǔ),代表內(nèi)部字段分隔符,它起源于Unixshell沒有內(nèi)置數(shù)組類型的時(shí)間。

作為解決方法,早期的Unixshell必須找到另一種表示多成員數(shù)組的方式。外殼程序?qū)⒁詥蝹€(gè)字符串將數(shù)組成員連接在一起,并以空格分隔。當(dāng)外殼程序需要檢索數(shù)組成員時(shí),它將字符串分成一個(gè)或多個(gè)字段。的$IFS變量告訴外殼程序在何處確切地中斷該字符串。

  外殼解釋$IFS如下字符:

如果值$IFS是空格,制表符和換行符,或者如果未設(shè)置變量,則在輸入的開頭或結(jié)尾處的空格,制表符或換行符的任何序列都將被忽略,并且輸入中這些字符的任何序列應(yīng)定界領(lǐng)域。

如果值$IFS為null,不得執(zhí)行任何字段拆分。

否則,應(yīng)依次適用以下規(guī)則:(a)$IFS在輸入的開頭和結(jié)尾應(yīng)忽略空格。(b)輸入中的每次出現(xiàn)$IFS不是的字符$IFS空白,以及任何相鄰的空白$IFS如前所述,空白應(yīng)界定一個(gè)字段。(c)非零長(zhǎng)度$IFS空白應(yīng)界定一個(gè)字段。

要執(zhí)行擴(kuò)展,我們將定義field_split()函數(shù),具有以下原型:

structword_s*field_split(char*str);

路徑名擴(kuò)展

在擴(kuò)展路徑名期間,shell將以給定的模式匹配一個(gè)或多個(gè)文件名。除特殊字符外,該模式還可以包含普通字符*,?和[],也稱為“通配符”。

星號(hào)*匹配任意長(zhǎng)度的字符,匹配一個(gè)字符,并且方括號(hào)引入正則表達(dá)式(RE)括號(hào)表達(dá)式。擴(kuò)展的結(jié)果是名稱與模式匹配的文件列表。

要執(zhí)行擴(kuò)展,我們將定義pathnames_expand()函數(shù),具有以下原型:

structword_s*pathnames_expand(structword_s*words);

此函數(shù)接受一個(gè)參數(shù):指向我們要路徑名擴(kuò)展的單詞的鏈接列表中第一個(gè)單詞的指針。對(duì)于每個(gè)單詞,該函數(shù)執(zhí)行以下操作:

檢查單詞是否包含任何通配符*,?和[],通過調(diào)用輔助函數(shù)has_glob_chars(),我們將在源文件中定義pattern.c。如果單詞包含通配符,我們將其視為需要匹配的模式;否則,我們移至下一個(gè)單詞。

獲取名稱與模式匹配的文件列表,不包括特殊名稱.和..。我們將模式匹配委托給另一個(gè)幫助函數(shù),get_filename_matches(),我們將在同一源文件中定義pattern.c。

將匹配的文件名添加到最終列表。

移至下一個(gè)單詞并循環(huán)。

返回與所有給定單詞匹配的文件名列表。

  刪除報(bào)價(jià)

單詞擴(kuò)展過程的最后一步是刪除引號(hào)。引用用于刪除某些字符到shell的特殊含義。外殼會(huì)以特殊方式處理某些字符,例如反斜杠和引號(hào)。要禁止這種行為,我們需要引用那些字符以強(qiáng)制外殼將它們視為普通字符。

我們可以使用以下三種方式之一對(duì)字符進(jìn)行引用:使用反斜杠,單引號(hào)或雙引號(hào)。反斜杠字符用于保留反斜杠后面的字符的字面意思。這類似于我們用C語(yǔ)言轉(zhuǎn)義字符的方式。

單引號(hào)保留引號(hào)內(nèi)所有字符的字面含義,即外殼程序不嘗試對(duì)單引號(hào)字符串進(jìn)行單詞擴(kuò)展。

雙引號(hào)與單引號(hào)類似,不同之處在于外殼可以識(shí)別反引號(hào),反斜杠和$標(biāo)志。也就是說,外殼程序可以在雙引號(hào)字符串內(nèi)執(zhí)行單詞擴(kuò)展。

要執(zhí)行報(bào)價(jià)刪除,我們將定義remove_quotes()函數(shù),具有以下原型:

voidremove_quotes(structword_s*wordlist)。

  放在一起

現(xiàn)在我們有了詞擴(kuò)展功能,是時(shí)候?qū)⑵浣Y(jié)合在一起了。在本節(jié)中,我們將編寫主要的單詞擴(kuò)展功能,我們將調(diào)用該功能來執(zhí)行單詞擴(kuò)展。反過來,此函數(shù)將調(diào)用其他函數(shù)來執(zhí)行單詞擴(kuò)展的各個(gè)步驟。

我們的主要功能是word_expand(),我們將在源文件中定義wordexp.c:

structword_s*word_expand(char*orig_word);

這是為了對(duì)作為唯一參數(shù)傳遞的單詞執(zhí)行單詞擴(kuò)展的功能:

創(chuàng)建原始單詞的副本。我們將在此副本上執(zhí)行單詞擴(kuò)展,以便在出現(xiàn)任何問題時(shí)將原始單詞保留完整。逐字掃描單詞,尋找特殊字符~,",',`,=,和$。如果找到上述字符之一,請(qǐng)致電substitute_word(),這將調(diào)用相應(yīng)的單詞擴(kuò)展功能。

跳過任何沒有特殊含義的字符。完成單詞擴(kuò)展后,通過調(diào)用執(zhí)行字段拆分field_split()。通過調(diào)用執(zhí)行路徑名擴(kuò)展pathnames_expand()。通過調(diào)用執(zhí)行報(bào)價(jià)刪除remove_quotes()。返回?cái)U(kuò)展單詞的列表。

更新掃描儀

在本教程的第二部分中,我們編寫了tokenize()函數(shù),我們用來獲取輸入令牌。到目前為止,我們的tokenize()函數(shù)不知道如何處理帶引號(hào)的字符串和轉(zhuǎn)義字符。要添加此功能,我們需要更新代碼。打開scanner.c文件,并將以下代碼添加到tokenize()功能之后switch聲明的開頭括號(hào):

case'"':

case''':

case'`':

add_to_buf(nc);

i=find_closing_quote(src->buffer+src->curpos);

if(!i)

{

src->curpos=src->bufsize;

fprintf(stderr,"error:missingclosingquote'%c' ",nc);

return&eof_token;

}

while(i--)

{

add_to_buf(next_char(src));

}

break;

case'\':

nc2=next_char(src);

if(nc2==' ')

{

break;

}

add_to_buf(nc);

if(nc2>0)

{

add_to_buf(nc2);

}

break;

case'$':

add_to_buf(nc);

nc=peek_char(src);

if(nc=='{'||nc=='(')

{

i=find_closing_brace(src->buffer+src->curpos+1);

if(!i)

{

src->curpos=src->bufsize;

fprintf(stderr,"error:missingclosingbrace'%c' ",nc);

return&eof_token;

}

while(i--)

{

add_to_buf(next_char(src));

}

}

elseif(isalnum(nc)||nc=='*'||nc=='@'||nc=='#'||

nc=='!'||nc=='?'||nc=='$')

{

add_to_buf(next_char(src));

}

break;

現(xiàn)在,我們的詞法掃描器知道如何識(shí)別和跳過帶引號(hào)的字符串,轉(zhuǎn)義字符和其他單詞擴(kuò)展構(gòu)造。在此鏈接中查看更新的詞法掃描程序代碼。

  更新執(zhí)行器最后,我們需要更新后端執(zhí)行程序,以便可以:

在執(zhí)行命令之前,對(duì)命令的參數(shù)執(zhí)行單詞擴(kuò)展。每個(gè)命令支持超過255個(gè)參數(shù)。打開executor.c文件,導(dǎo)航到do_simple_command()函數(shù)并找到以下幾行:

intargc=0;

longmax_args=255;

char*argv[max_args+1];

char*str;

while(child)

{

...

}

argv[argc]=NULL;

并用以下代碼替換它們:

intargc=0;

inttargc=0;

char**argv=NULL;

char*str;

while(child)

{

str=child->val.str;

structword_s*w=word_expand(str);

if(!w)

{

child=child->next_sibling;

continue;

}

structword_s*w2=w;

while(w2)

{

if(check_buffer_bounds(&argc,&targc,&argv))

{

str=malloc(strlen(w2->data)+1);

if(str)

{

strcpy(str,w2->data);

argv[argc++]=str;

}

}

w2=w2->next;

}

free_all_words(w);

child=child->next_sibling;

}

if(check_buffer_bounds(&argc,&targc,&argv))

{

argv[argc]=NULL;

}

使用此代碼,執(zhí)行程序調(diào)用word_expand()在每個(gè)命令自變量上,并將擴(kuò)展的單詞添加到自變量列表,我們最終將其傳遞給命令。該列表可以根據(jù)需要增長(zhǎng),這要?dú)w功于我們check_buffer_bounds()函數(shù),根據(jù)需要將內(nèi)存分配給緩沖區(qū)。

現(xiàn)在剩下的就是在執(zhí)行完命令后釋放參數(shù)列表,然后返回調(diào)用者。為此我們致電:

free_buffer(argc,argv);

在三個(gè)不同的位置:執(zhí)行內(nèi)置實(shí)用程序后,如果fork()返回錯(cuò)誤狀態(tài),然后waitpid()已經(jīng)回來了。在此鏈接中查看更新的執(zhí)行程序代碼。

  編譯外殼

讓我們編譯一下shell。打開您喜歡的終端模擬器,導(dǎo)航到源目錄,并確保其中有19個(gè)文件和2個(gè)子目錄:

現(xiàn)在,使用以下命令編譯外殼程序:

  make

如果一切順利gcc不應(yīng)輸出任何內(nèi)容,并且應(yīng)該有一個(gè)名為shell在當(dāng)前目錄中:

現(xiàn)在通過運(yùn)行來調(diào)用shell./shell,然后嘗試使用我們的單詞擴(kuò)展功能并檢查結(jié)果:$echo*Makefilebuildbuiltinsexecutor.cexecutor.hinitsh.cmain.cnode.cnode.hparser.cparser.hpattern.cprompt.cscanner.cscanner.hshellshell.hshunt.csource.csource.hstrings.csymtabwordexp.c

$echo'*'

*

$echo~

/home/user

$echo~/Downloads

/home/user/Downloads

$echo${A=value}

value

$echo$A

value

$echo$((2+7))

9

就是今天。我們的外殼現(xiàn)在可以處理各種單詞擴(kuò)展。玩弄外殼,看看從不同類型的單詞擴(kuò)展中可以得到什么結(jié)果。將結(jié)果與從默認(rèn)Shell獲得的結(jié)果進(jìn)行比較。

在此,我們已經(jīng)取得了長(zhǎng)足的進(jìn)步,提供了許多代碼,其中大多數(shù)代碼沒有時(shí)間或空間來詳細(xì)研究。您可能需要花一些時(shí)間閱讀我們存儲(chǔ)庫(kù)中的代碼,以使自己熟悉單詞擴(kuò)展過程。想了解更多關(guān)于Linux Shell 的信息,請(qǐng)繼續(xù)關(guān)注中培偉業(yè)。

標(biāo)簽: IT運(yùn)維
主站蜘蛛池模板: 在线观看片免费视频无码 | 国产成人香蕉久久久久 | 2021亚洲卡一卡二新区入口 | 97人妻人人做人碰人人添 | 亚洲国产综合精品中久 | 亚洲中文字幕av无码专区 | 一本大道精品视频在线 | 国内美女人妻一级毛片免费看 | 蜜桃色欲AV久久无码精品 | 精品久久久无码专区中文字幕 | 欧美成人性a片免费观看办公室 | 久久精品中文无码资源站 | 国产肉体XXXX裸体XXXX | 性xxxx视频播放 | 日本真人无遮挡啪啪免费 | 999精品一区 | 奶头好大揉着好爽视频午夜院 | 男人的天堂日韩 | 亚洲AV元码天堂一区二区三区 | 亚洲无人区码一码二码三码的特点 | beeg最新版欧美 | 亚洲AV无码精品无码麻豆 | 又色又爽又黄高潮的免费视频 | 红桃影视成人免费 | 国精产品一区二区三区有限公司 | 国产人妻久久精品一区二区三区 | 城中村快餐嫖老妇对白 | 夜夜夜夜曰天天天天拍国产 | 国产乱人伦精品一区二区三区 | 日本裸体猜人综艺节目在线 | 欧美视频在线观看一区二区 | AV未满十八禁免费网站 | 午夜激情久久久 | 色婷婷亚洲十月十月色天 | 97久久人人超碰超碰窝窝 | 亚洲乱码中文字幕精品久久 | 欧美日韩一区二区三区视频播放 | 中国少妇无码专区 | 男女18禁啪啪无遮挡激烈 | 夜夜偷天天爽夜夜爱 | 日本AAAAA级特黄大片 |