這是有關如何構建Linux Shell的教程的第四部分。在這一部分中,我們將向我們的外殼添加符號表。的符號表是用于由數據結構的編譯器和解釋器來存儲變量如表中的條目。每個條目都包含一個鍵(變量的名稱)和一個關聯的值(變量的值)。鍵通常是唯一的,也就是說,我們不能有兩個共享相同鍵的條目(即,不能有兩個共享相同變量名的變量)。
通常,Linux Shell在啟動時會填充其符號表。填充符號表后,編譯器或解釋器可以輕松地在表中搜索變量以檢索該變量的值。我們還可以執行類型檢查,執行作用域規則(例如,使變量僅對聲明其的函數可見),并將shell變量導出到外部命令。
為了填充符號表,外殼程序讀取環境變量列表,該環境變量列表從其父進程(通常是登錄用戶的進程或登錄進程的子進程)傳遞到外殼程序。Shell將每個變量(及其值)添加到符號表中。然后,我們可以使用適當的內置實用程序隨意編輯,刪除或導出shell變量(我們將在本系列的稍后部分中討論)。
為什么我們需要符號表?
簡而言之,符號表使我們能夠定義外殼變量,修改它們的值,在執行變量擴展時使用不同外殼變量的值以及將變量導出到外部命令。在本系列后面的內容中,當我們討論位置和特殊外殼參數時,符號表也將變得很方便。
每當您要求外殼程序回顯,導出或未設置外殼程序變量的值時,您實際上就是在要求外殼程序訪問和/或修改其符號表。所有外殼程序都有某種符號表實現,盡管某些外殼程序可能具有不同的名稱。
例如,假設您調用了以下命令:
echo $PATH
哪個應該給你類似的輸出:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
您可能知道 echo 該命令與您在屏幕上看到的輸出無關,除了以下事實: echo打印出路徑。它的外殼究竟是誰明白$PATH 代表外殼變量名稱。
也是貝殼代替了單詞$PATH 帶有實際路徑值,然后將其傳遞給 echo。的echo 命令只是回顯了外殼程序傳遞的參數,這是您在屏幕上看到的可執行路徑。
因此,為了能夠定義,修改,取消設置和導出Shell變量,我們首先需要實現符號表。讓我們看看接下來如何做。
實施符號表
有多種方法可以實現符號表,常見的方法是鏈表,哈希表和二進制搜索樹。每種方法都有優點和缺點,我們沒有時間或空間來詳細討論每種方法。為了我們的目的,我們將使用鏈表,鏈表是最容易實現的,并且在訪問速度和內存使用方面都相當不錯。
(注:如果你想使用的外殼任何東西比學習其他的,你應該考慮改變符號表執行到使用哈希表或二進制樹可以找到哈希表實現的例子這里)。
現在,讓我們來破解該代碼。在您的源目錄中,創建一個名為symtab (調用 mkdir symtab從您的終端仿真器)。導航到該目錄(cd symtab)并創建一個名為 symtab.h。將以下代碼添加到剛創建的頭文件中:
#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 表示函數(在本系列后面的部分中,我們將介紹shell函數)。
的 struct symtab_entry_s結構代表我們的符號表條目。該結構包含以下字段:
.name =>此條目表示的shell變量(或函數)的名稱。
.val_type => SYM_STR 對于外殼變量, SYM_FUNC 用于外殼函數。
.val =>字符串值(僅適用于Shell變量)。
.flags =>表示我們將分配給變量和函數的不同屬性,例如export和readonly標志(我們將在本系列的后面部分處理這些標志)。
.next =>指向下一個符號表條目的指針(因為我們將表實現為單鏈接列表)。
.func_body=>對于外殼函數,是函數主體的抽象語法樹或AST(我們在本教程的第一部分中討論了AST )。
的 struct symtab_s結構表示單個符號表。首先,我們將使用一個符號表,在其中定義所有的shell變量。稍后,當我們討論外殼函數并開始使用腳本文件時,我們將需要定義更多的符號表。
第零個符號表將是全局表,在其中我們將定義全局變量(shell可以訪問的全局變量,以及由它執行的所有函數和腳本)。
符號表中排名第一的符號表是本地表,我們將在其中定義我們的本地變量(這些變量只能由聲明了它們的shell函數或腳本訪問)。通過以這種方式級聯符號表,我們有效地實現了變量作用域。
我們的 struct symtab_s 結構包含以下字段:
.level =>對于全局符號表為0,對于局部符號表為1及更高。
.first, last =>分別指向表的鏈表中第一個和最后一個條目的指針。
現在,要能夠如上所述層疊符號表,我們需要定義并實現符號表棧。甲堆棧是一個后進先出,或LIFO,數據結構,其中的最后一個項目中加入(或推)是移除(或第一項彈出)。的struct symtab_stack_s結構代表我們的符號表堆棧。該結構包含以下字段:
.symtab_count =>當前堆棧中符號表的數量。
.symtab_list=>指向堆棧符號表的指針數組。第零項指向全局符號表,而symtab_count-1項目指向最后一個(或本地)符號表。堆棧最多可容納MAX_SYMTAB 項,我們在頭文件的開頭將其定義為256。
.global_symtab, local_symtab =>分別指向全局和局部符號表的指針(為了易于訪問)。
我們將在本課程的稍后部分實現堆棧。現在,我們將從編寫使用符號表所需的功能開始。
符號表功能
創建 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 =>我們當前在堆棧中的級別(如果正在使用全局符號表,則為0,否則為非零)。
的 init_symtab() 函數初始化符號表堆棧,然后為全局符號表分配內存并進行初始化。
接下來,添加以下功能:
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() 每當我們想要創建一個新的符號表時(例如,當我們要執行一個shell函數時),函數就起作用。
接下來,添加以下功能:
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() 當我們完成了符號表的工作后,我們想使用該函數,并希望釋放符號表及其條目所使用的內存。
接下來,我們將定義一個調試功能:
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, " ");
}
此功能打印本地符號表的內容。當我們的外殼啟動時,本地和全局符號表將引用同一表。只有在Shell要運行Shell函數或腳本文件時,我們的本地表才與全局表不同。(在本課程后面,我們將編寫一個內置實用程序,該實用程序將調用dump_local_symtab() 以幫助我們可視化外殼的全局符號表的內容)。
現在,讓我們定義一些函數來幫助我們處理符號表條目。在同一文件中(symtab.c),添加以下功能:
struct symtab_entry_s *add_to_symtab(char *symbol)
{
if(!symbol || symbol[0] == ' 主站蜘蛛池模板: 国产高清视频在线观看一区二区 | 亚洲成年在线 | 美女视频黄频A免费 | 丰满少妇高潮久久三区 | 波多野结衣在线播放一区 | 精品无码中文视频在线观看 | 亚洲精品一区二区三区免 | 999精品嫩草久久久久久99 | 亚洲国产亚洲国产亚洲 | 久久国产精品成人影院 | 久久成人无码影片 | 国产在线欧美日韩 | 日本免费AⅤ欧美在线观看 无码国内精品人妻少妇蜜桃视频 | 国产熟人精品一区二区 | 99好久被狂躁A片视频无码刻晴 | 国产成人aa在线视频 | 国产99久久久精品 | 香蕉99久久国产综合精品宅男自 | 亚洲欧美综合精品久久成人网无毒不卡 | аⅴ中文在线天堂 | 久久精品99久久无色码中文字幕 | 完美世界免费观看完整 | 欧美日韩国产不卡 | 中文永久有效幕中文永久 | 国产日韩欧美在线 | 无码高潮又爽又黄A片软件 精品国产AV无码一区二区三区 | 国产精品爆乳在线播放 | 一区中文 | 丝袜美女被遭强高潮网站 | 天堂在线www资源在线 | 国产裸体舞一区二区三区 | 日本韩国欧美在线 | 2017最新高清无码网站 | 在线?看免费91 | 色花堂永久网站 | 麻豆xx| 久久五月亭| 风韵丰满熟妇啪啪区老熟熟女 | 男人插女人免费视频 | 2020最新无码福利视频 | 丰满日韩放荡少妇无码视频 |