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

中培偉業IT資訊頻道
您現在的位置:首頁 > IT資訊 > IT運維 > 如何構建一個Linux Shell(二)

如何構建一個Linux Shell(二)

2020-07-24 17:14:17 | 來源:中培企業IT培訓網

在如何構建一個Linux Shell(一)中,我們構建了一個簡單的Linux shell,該shell打印提示字符串,讀取輸入,然后將輸入回顯到屏幕上。現在這不是很令人印象深刻,不是嗎?在如何構建一個Linux Shell(二)中,我們將更新代碼,以使Shell能夠解析和執行簡單命令。首先讓我們看一下什么是簡單的命令。

  什么是簡單命令?

一個簡單的命令 由單詞列表組成,這些單詞列表由空格字符(空格,制表符,換行符)分隔。第一個單詞是命令名,并且是必需的(否則,shell將沒有解析和執行命令!)。第二個和后續單詞是可選的。如果存在,它們形成的論點,我們希望shell傳遞到執行的命令。

例如,以下命令: ls -l 由兩個詞組成: ls (命令名稱),以及 -l(第一個也是唯一的參數)。同樣,命令:gcc -o shell main.c prompt.c(在第一部分中,我們用它來編譯我們的shell)由5個詞組成:一個命令名和一個4個參數的列表。

為了能夠執行簡單的命令,我們的外殼程序需要執行以下步驟:

掃描輸入,一次輸入一個字符,以查找下一個標記。我們稱此過程為詞法掃描,而執行此任務的外殼部分稱為詞法掃描器,或簡稱為掃描器。

提取輸入令牌。我們稱這種標記化輸入。

解析標記并創建抽象語法樹或AST。Shell負責執行此操作的部分稱為解析器。

執行AST。這是執行者的工作。

下圖顯示了Shell為了解析和執行命令而執行的步驟。您可以看到圖中包含的步驟比上面列表中顯示的步驟更多,這很好。隨著外殼的增長和變得越來越復雜,我們將在需要時添加其他步驟。

現在,讓我們詳細查看上述四個步驟,并查看在shell中實現這些功能所需的代碼。

  掃描輸入

為了獲得下一個令牌,我們需要能夠一次掃描一個字符的輸入,以便我們可以識別可以作為令牌一部分的字符和作為定界符的字符。甲分隔符是一個標記的令牌(以及可能的另一令牌的開始)的端部。通常,分隔符是空格字符(空格,制表符,換行符),但也可以包含其他字符,例如; 和 &。

通常,掃描輸入意味著我們應該能夠:

.從輸入中檢索下一個字符。

.返回我們讀回的最后一個字符作為輸入。

.前瞻(或窺視)以檢查下一個字符,而無需實際檢索它。

.跳過空白字符。

我們將在一分鐘內定義執行所有這些任務的功能。但是首先,讓我們談談抽象輸入。

記住 read_cmd()函數,這是我們在本教程的第一部分中定義的?那就是我們用來讀取用戶輸入并將其作為malloc的字符串。我們可以將此字符串直接傳遞給我們的掃描儀,但這會使掃描過程有點麻煩。特別是,掃描器很難記住它給我們的最后一個字符,以便它可以越過該字符并給我們后面的字符。

為了簡化掃描儀的工作,我們通過將輸入字符串作為 struct source_s 結構,我們將在源文件中定義 source.h。繼續在源目錄中創建該文件,然后在您喜歡的文本編輯器中將其打開并添加以下代碼:

#ifndef SOURCE_H

#define SOURCE_H

#define EOF (-1)

#define ERRCHAR ( 0)

#define INIT_SRC_POS (-2)

struct source_s

{

char *buffer; /* the input text */

long bufsize; /* size of the input text */

long curpos; /* absolute char position in source */

};

char next_char(struct source_s *src);

void unget_char(struct source_s *src);

char peek_char(struct source_s *src);

void skip_white_spaces(struct source_s *src);

#endif

關注結構的定義,您可以看到 struct source_s 除了兩個以外,還包含指向輸入字符串的指針 long 包含有關字符串長度和我們當前在字符串中的位置(將從中獲取下一個字符)的信息的字段。

現在創建另一個名為 source.c,您應在其中添加以下代碼:

#include

#include "shell.h"

#include "source.h"

void unget_char(struct source_s *src)

{

if(src->curpos < 0)

{

return;

}

src->curpos--;

}

char next_char(struct source_s *src)

{

if(!src || !src->buffer)

{

errno = ENODATA;

return ERRCHAR;

}

char c1 = 0;

if(src->curpos == INIT_SRC_POS)

{

src->curpos = -1;

}

else

{

c1 = src->buffer[src->curpos];

}

if(++src->curpos >= src->bufsize)

{

src->curpos = src->bufsize;

return EOF;

}

return src->buffer[src->curpos];

}

char peek_char(struct source_s *src)

{

if(!src || !src->buffer)

{

errno = ENODATA;

return ERRCHAR;

}

long pos = src->curpos;

if(pos == INIT_SRC_POS)

{

pos++;

}

pos++;

if(pos >= src->bufsize)

{

return EOF;

}

return src->buffer[pos];

}

void skip_white_spaces(struct source_s *src)

{

char c;

if(!src || !src->buffer)

{

return;

}

while(((c = peek_char(src)) != EOF) && (c == ' ' || c == ' '))

{

next_char(src);

}

}

的 unget_char()函數將(我們從輸入中檢索到的)最后一個字符返回(或取消保護)到輸入源。它只是通過操縱源結構的指針來做到這一點。在本系列后面的部分中,您將看到此功能的好處。

的 next_char() 函數返回輸入的下一個字符并更新源指針,以便下一次調用 next_char()返回以下輸入字符。當我們到達輸入中的最后一個字符時,該函數將返回特殊字符EOF,我們在其中將其定義為-1 source.h 以上。

的 peek_char() 功能類似于 next_char()它返回輸入的下一個字符。唯一的區別是peek_char() 不會更新源指針,因此下一次調用 next_char()返回我們剛剛偷看的相同輸入字符。在本系列的后面部分,您將看到輸入偷看的好處。

最后, skip_white_spaces()函數將跳過所有空格字符。這將在完成讀取令牌后為我們提供幫助,并且在讀取下一個令牌之前希望跳過定界符空白。

  標記輸入

現在我們已經有了掃描儀的功能,我們將使用這些功能來提取輸入令牌。我們將首先定義一個新結構,該結構將用于表示令牌。

繼續創建一個名為 scanner.h 在您的源目錄中,然后將其打開并添加以下代碼:

#ifndef SCANNER_H

#define SCANNER_H

struct token_s

{

struct source_s *src; /* source of input */

int text_len; /* length of token text */

char *text; /* token text */

};

/* the special EOF token, which indicates the end of input */

extern struct token_s eof_token;

struct token_s *tokenize(struct source_s *src);

void free_token(struct token_s *tok);

#endif

專注于結構定義, struct token_s 包含一個指向 struct source_s保留了我們的投入。該結構還包含一個指向令牌文本的指針,以及一個告訴我們該文本長度的字段(這樣我們就無需重復調用strlen() 在令牌的文本上)。

接下來,我們將編寫 tokenize()函數,它將從輸入中檢索下一個標記。我們還將編寫一些幫助程序功能,以幫助我們使用輸入令牌。

在源目錄中,創建一個名為 scanner.c,然后輸入以下代碼:

#include

#include

#include

#include

#include "shell.h"

#include "scanner.h"

#include "source.h"

char *tok_buf = NULL;

int tok_bufsize = 0;

int tok_bufindex = -1;

/* special token to indicate end of input */

struct token_s eof_token =

{

.text_len = 0,

};

void add_to_buf(char c)

{

tok_buf[tok_bufindex++] = c;

if(tok_bufindex >= tok_bufsize)

{

char *tmp = realloc(tok_buf, tok_bufsize*2);

if(!tmp)

{

errno = ENOMEM;

return;

}

tok_buf = tmp;

tok_bufsize *= 2;

}

}

struct token_s *create_token(char *str)

{

struct token_s *tok = malloc(sizeof(struct token_s));

if(!tok)

{

return NULL;

}

memset(tok, 0, sizeof(struct token_s));

tok->text_len = strlen(str);

char *nstr = malloc(tok->text_len+1);

if(!nstr)

{

free(tok);

return NULL;

}

strcpy(nstr, str);

tok->text = nstr;

return tok;

}

void free_token(struct token_s *tok)

{

if(tok->text)

{

free(tok->text);

}

free(tok);

}

struct token_s *tokenize(struct source_s *src)

{

int endloop = 0;

if(!src || !src->buffer || !src->bufsize)

{

errno = ENODATA;

return &eof_token;

}

if(!tok_buf)

{

tok_bufsize = 1024;

tok_buf = malloc(tok_bufsize);

if(!tok_buf)

{

errno = ENOMEM;

return &eof_token;

}

}

tok_bufindex = 0;

tok_buf[0] = '

主站蜘蛛池模板: 无码人妻丰满熟妇啪啪网站 | 337p日本欧洲亚洲大胆艺术96 | 又大又黄又硬视频 | 久久久久人妻一区精品色欧美 | 人妻阿敏被老外玩弄系列 | 欧美一区二区三区视频在线观看 | 日本乱偷人妻中文字幕 | 中国人与黑人牲交FREE欧美 | 慈禧一级淫片免费放特级 | 亚洲精品网站在线观看你懂的 | 国产精品IGAO视频网 | 一级毛片一级毛片 | 久久夜色精品国产亚洲av | 天堂素人约啪 | 黄色片子免费观看 | 女人被躁到高潮嗷嗷叫游戏 | 亚洲精品国产一区二区三区在线观看 | 多人运动免费观看不用登录 | 女人被强╳到高潮喷水在线观看 | 9 9久热RE在线精品视频 | 亚洲成AV人在线观看网站 | 亚洲avav国产av综合av | 日韩精品无码人妻免费视频 | 男吃奶玩乳尖高潮视频 | 高清freesexmovies性tv出水 | 国产露脸无码A区久久 | 国产精品黑色丝袜在线观看 | 大地资源在线观看官网第三页 | 欧美性猛交XXXX乱大交 | 漂亮人妻洗澡被公强啪啪 | 人人澡人人澡人人 | 日本丰满老妇BBW | 一夲道av无码无卡免费 | 国产山东熟女48嗷嗷叫 | 无码中文av波多野结衣 | 亚洲阿V天堂网2019无码 | 免费看片成人 | 青青久在线视频免费观看 | 国产产区一二三产区区别在线 | 亚洲中文无码线在线观看 | 美女扒开下面让男生桶白浆 |