這是有關(guān)如何構(gòu)建Linux Shell的教程的第四部分。在這一部分中,我們將向我們的外殼添加符號表。的符號表是用于由數(shù)據(jù)結(jié)構(gòu)的編譯器和解釋器來存儲變量如表中的條目。每個條目都包含一個鍵(變量的名稱)和一個關(guān)聯(lián)的值(變量的值)。鍵通常是唯一的,也就是說,我們不能有兩個共享相同鍵的條目(即,不能有兩個共享相同變量名的變量)。
通常,Linux Shell在啟動時會填充其符號表。填充符號表后,編譯器或解釋器可以輕松地在表中搜索變量以檢索該變量的值。我們還可以執(zhí)行類型檢查,執(zhí)行作用域規(guī)則(例如,使變量僅對聲明其的函數(shù)可見),并將shell變量導(dǎo)出到外部命令。
為了填充符號表,外殼程序讀取環(huán)境變量列表,該環(huán)境變量列表從其父進(jìn)程(通常是登錄用戶的進(jìn)程或登錄進(jìn)程的子進(jìn)程)傳遞到外殼程序。Shell將每個變量(及其值)添加到符號表中。然后,我們可以使用適當(dāng)?shù)膬?nèi)置實(shí)用程序隨意編輯,刪除或?qū)С鰏hell變量(我們將在本系列的稍后部分中討論)。
為什么我們需要符號表?
簡而言之,符號表使我們能夠定義外殼變量,修改它們的值,在執(zhí)行變量擴(kuò)展時使用不同外殼變量的值以及將變量導(dǎo)出到外部命令。在本系列后面的內(nèi)容中,當(dāng)我們討論位置和特殊外殼參數(shù)時,符號表也將變得很方便。
每當(dāng)您要求外殼程序回顯,導(dǎo)出或未設(shè)置外殼程序變量的值時,您實(shí)際上就是在要求外殼程序訪問和/或修改其符號表。所有外殼程序都有某種符號表實(shí)現(xiàn),盡管某些外殼程序可能具有不同的名稱。
例如,假設(shè)您調(diào)用了以下命令:
echo $PATH
哪個應(yīng)該給你類似的輸出:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
您可能知道 echo 該命令與您在屏幕上看到的輸出無關(guān),除了以下事實(shí): echo打印出路徑。它的外殼究竟是誰明白$PATH 代表外殼變量名稱。
也是貝殼代替了單詞$PATH 帶有實(shí)際路徑值,然后將其傳遞給 echo。的echo 命令只是回顯了外殼程序傳遞的參數(shù),這是您在屏幕上看到的可執(zhí)行路徑。
因此,為了能夠定義,修改,取消設(shè)置和導(dǎo)出Shell變量,我們首先需要實(shí)現(xiàn)符號表。讓我們看看接下來如何做。
實(shí)施符號表
有多種方法可以實(shí)現(xiàn)符號表,常見的方法是鏈表,哈希表和二進(jìn)制搜索樹。每種方法都有優(yōu)點(diǎn)和缺點(diǎn),我們沒有時間或空間來詳細(xì)討論每種方法。為了我們的目的,我們將使用鏈表,鏈表是最容易實(shí)現(xiàn)的,并且在訪問速度和內(nèi)存使用方面都相當(dāng)不錯。
(注:如果你想使用的外殼任何東西比學(xué)習(xí)其他的,你應(yīng)該考慮改變符號表執(zhí)行到使用哈希表或二進(jìn)制樹可以找到哈希表實(shí)現(xiàn)的例子這里)。
現(xiàn)在,讓我們來破解該代碼。在您的源目錄中,創(chuàng)建一個名為symtab (調(diào)用 mkdir symtab從您的終端仿真器)。導(dǎo)航到該目錄(cd symtab)并創(chuàng)建一個名為 symtab.h。將以下代碼添加到剛創(chuàng)建的頭文件中:
#ifndef SYMTAB_H
#define SYMTAB_H
#include "../node.h"
#define MAX_SYMTAB 256
/* the type of a symbol table entry's value */
enum symbol_type_e
{
SYM_STR ,
SYM_FUNC,
};
/* the symbol table entry structure */
struct symtab_entry_s
{
char *name;
enum symbol_type_e val_type;
char *val;
unsigned int flags;
struct symtab_entry_s *next;
struct node_s *func_body;
};
/* the symbol table structure */
struct symtab_s
{
int level;
struct symtab_entry_s *first, *last;
};
/* values for the flags field of struct symtab_entry_s */
#define FLAG_EXPORT (1 << 0) /* export entry to forked commands */
/* the symbol table stack structure */
struct symtab_stack_s
{
int symtab_count;
struct symtab_s *symtab_list[MAX_SYMTAB];
struct symtab_s *global_symtab, *local_symtab;
};
struct symtab_s *new_symtab(int level);
struct symtab_s *symtab_stack_push(void);
struct symtab_s *symtab_stack_pop(void);
int rem_from_symtab(struct symtab_entry_s *entry, struct symtab_s *symtab);
struct symtab_entry_s *add_to_symtab(char *symbol);
struct symtab_entry_s *do_lookup(char *str, struct symtab_s *symtable);
struct symtab_entry_s *get_symtab_entry(char *str);
struct symtab_s *get_local_symtab(void);
struct symtab_s *get_global_symtab(void);
struct symtab_stack_s *get_symtab_stack(void);
void init_symtab(void);
void dump_local_symtab(void);
void free_symtab(struct symtab_s *symtab);
void symtab_entry_setval(struct symtab_entry_s *entry, char *val);
#endif
的 symbol_type_e枚舉定義了我們的符號表條目的類型。我們將使用類型SYM_STR 表示外殼變量,以及 SYM_FUNC 表示函數(shù)(在本系列后面的部分中,我們將介紹shell函數(shù))。
的 struct symtab_entry_s結(jié)構(gòu)代表我們的符號表條目。該結(jié)構(gòu)包含以下字段:
.name =>此條目表示的shell變量(或函數(shù))的名稱。
.val_type => SYM_STR 對于外殼變量, SYM_FUNC 用于外殼函數(shù)。
.val =>字符串值(僅適用于Shell變量)。
.flags =>表示我們將分配給變量和函數(shù)的不同屬性,例如export和readonly標(biāo)志(我們將在本系列的后面部分處理這些標(biāo)志)。
.next =>指向下一個符號表條目的指針(因為我們將表實(shí)現(xiàn)為單鏈接列表)。
.func_body=>對于外殼函數(shù),是函數(shù)主體的抽象語法樹或AST(我們在本教程的第一部分中討論了AST )。
的 struct symtab_s結(jié)構(gòu)表示單個符號表。首先,我們將使用一個符號表,在其中定義所有的shell變量。稍后,當(dāng)我們討論外殼函數(shù)并開始使用腳本文件時,我們將需要定義更多的符號表。
第零個符號表將是全局表,在其中我們將定義全局變量(shell可以訪問的全局變量,以及由它執(zhí)行的所有函數(shù)和腳本)。
符號表中排名第一的符號表是本地表,我們將在其中定義我們的本地變量(這些變量只能由聲明了它們的shell函數(shù)或腳本訪問)。通過以這種方式級聯(lián)符號表,我們有效地實(shí)現(xiàn)了變量作用域。
我們的 struct symtab_s 結(jié)構(gòu)包含以下字段:
.level =>對于全局符號表為0,對于局部符號表為1及更高。
.first, last =>分別指向表的鏈表中第一個和最后一個條目的指針。
現(xiàn)在,要能夠如上所述層疊符號表,我們需要定義并實(shí)現(xiàn)符號表棧。甲堆棧是一個后進(jìn)先出,或LIFO,數(shù)據(jù)結(jié)構(gòu),其中的最后一個項目中加入(或推)是移除(或第一項彈出)。的struct symtab_stack_s結(jié)構(gòu)代表我們的符號表堆棧。該結(jié)構(gòu)包含以下字段:
.symtab_count =>當(dāng)前堆棧中符號表的數(shù)量。
.symtab_list=>指向堆棧符號表的指針數(shù)組。第零項指向全局符號表,而symtab_count-1項目指向最后一個(或本地)符號表。堆棧最多可容納MAX_SYMTAB 項,我們在頭文件的開頭將其定義為256。
.global_symtab, local_symtab =>分別指向全局和局部符號表的指針(為了易于訪問)。
我們將在本課程的稍后部分實(shí)現(xiàn)堆棧。現(xiàn)在,我們將從編寫使用符號表所需的功能開始。
符號表功能
創(chuàng)建 symtab.c 文件(在 symtab 子目錄),然后添加以下代碼開始:
#include
#include
#include
#include "../shell.h"
#include "../node.h"
#include "../parser.h"
#include "symtab.h"
struct symtab_stack_s symtab_stack;
int symtab_level;
void init_symtab(void)
{
symtab_stack.symtab_count = 1;
symtab_level = 0;
struct symtab_s *global_symtab = malloc(sizeof(struct symtab_s));
if(!global_symtab)
{
fprintf(stderr, "fatal error: no memory for global symbol table ");
exit(EXIT_FAILURE);
}
memset(global_symtab, 0, sizeof(struct symtab_s));
symtab_stack.global_symtab = global_symtab;
symtab_stack.local_symtab = global_symtab;
symtab_stack.symtab_list[0] = global_symtab;
global_symtab->level = 0;
}
首先,我們有兩個全局變量:
.symtab_stack =>指向符號表堆棧的指針(每個外殼僅需要一個堆棧)。
.symtab_level =>我們當(dāng)前在堆棧中的級別(如果正在使用全局符號表,則為0,否則為非零)。
的 init_symtab() 函數(shù)初始化符號表堆棧,然后為全局符號表分配內(nèi)存并進(jìn)行初始化。
接下來,添加以下功能:
struct symtab_s *new_symtab(int level)
{
struct symtab_s *symtab = malloc(sizeof(struct symtab_s));
if(!symtab)
{
fprintf(stderr, "fatal error: no memory for new symbol table ");
exit(EXIT_FAILURE);
}
memset(symtab, 0, sizeof(struct symtab_s));
symtab->level = level;
return symtab;
}
我們稱 new_symtab() 每當(dāng)我們想要創(chuàng)建一個新的符號表時(例如,當(dāng)我們要執(zhí)行一個shell函數(shù)時),函數(shù)就起作用。
接下來,添加以下功能:
void free_symtab(struct symtab_s *symtab)
{
if(symtab == NULL)
{
return;
}
struct symtab_entry_s *entry = symtab->first;
while(entry)
{
if(entry->name)
{
free(entry->name);
}
if(entry->val)
{
free(entry->val);
}
if(entry->func_body)
{
free_node_tree(entry->func_body);
}
struct symtab_entry_s *next = entry->next;
free(entry);
entry = next;
}
free(symtab);
}
我們稱 free_symtab() 當(dāng)我們完成了符號表的工作后,我們想使用該函數(shù),并希望釋放符號表及其條目所使用的內(nèi)存。
接下來,我們將定義一個調(diào)試功能:
void dump_local_symtab(void)
{
struct symtab_s *symtab = symtab_stack.local_symtab;
int i = 0;
int indent = symtab->level * 4;
fprintf(stderr, "%*sSymbol table [Level %d]: ", indent, " ", symtab->level);
fprintf(stderr, "%*s=========================== ", indent, " ");
fprintf(stderr, "%*s No Symbol Val ", indent, " ");
fprintf(stderr, "%*s------ -------------------------------- ------------ ", indent, " ");
struct symtab_entry_s *entry = symtab->first;
while(entry)
{
fprintf(stderr, "%*s[%04d] %-32s '%s' ", indent, " ",
i++, entry->name, entry->val);
entry = entry->next;
}
fprintf(stderr, "%*s------ -------------------------------- ------------ ", indent, " ");
}
此功能打印本地符號表的內(nèi)容。當(dāng)我們的外殼啟動時,本地和全局符號表將引用同一表。只有在Shell要運(yùn)行Shell函數(shù)或腳本文件時,我們的本地表才與全局表不同。(在本課程后面,我們將編寫一個內(nèi)置實(shí)用程序,該實(shí)用程序?qū)⒄{(diào)用dump_local_symtab() 以幫助我們可視化外殼的全局符號表的內(nèi)容)。
現(xiàn)在,讓我們定義一些函數(shù)來幫助我們處理符號表條目。在同一文件中(symtab.c),添加以下功能:
struct symtab_entry_s *add_to_symtab(char *symbol)
{
if(!symbol || symbol[0] == ' 主站蜘蛛池模板: 日韩精品福利在线 | 女女互磨互喷水高潮les呻吟 | 漂亮人妇中出中文字幕在线 | 91久久国产精品视频 | 女久久久| 中文字幕+乱码+中文字幕无忧 | 日本高清免费看 | 啊啊啊啊亚洲 | 九九精品视频免费 | av观看网| 亚洲激情视频免费观看 | 少妇bbbbb撒尿视频 | av免费在线免费观看 | 国产欧美大片 | 91怡红院 | 精69xxxxxx在线观看 | 久久网中文字幕 | 国产一级aaa毛片 | 亚洲无人区码一码二码三码的特点 | 中文高清av | 一级久久19久久久区区区区区区 | 人人精品| 俄罗斯xxxx性全过程 | 看毛片网 | 日本欧美大码aⅴ在线播放 亚洲靠逼网站 | 99久热re在线精品99re8热视频 | 久久久婷婷白浆水 | av综合一区 | 国产一级爽快片在线观看 | 男人大臿蕉香蕉大视频 | 国产成人精品一区二区在线小狼 | 毛片啪啪 | xxxx69在线观看 | 小14萝裸体洗澡视频免费网站 | 色玖玖综合 | 亚洲欧美乱日韩乱国产 | 久久精品国产亚洲aa级女大片 | 国产精品丝袜久久久久久不卡 | 久久精品欧美日韩精品 | 久久成人人人人精品欧 | 99在线免费观看 |