欧美麻豆久久久久久中文_成年免费观看_男人天堂亚洲成人_中国一级片_动漫黄网站免费永久在线观看_国产精品自产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] = '

主站蜘蛛池模板: 国产成人人妻精品一区二区三区 | 在线视频免费观看www | 18禁免费观看网站 | 精品无人区无码乱码大片国产 | 国产无线卡一卡二 | 久久久精品人妻无码专区不卡 | 亚洲精品第一国产综合精品99 | 少妇A级裸片AAAAA八戒 | 日韩欧美亚洲综合久久 | 久久精品女人天堂AV | 国产交换配乱婬视频偷 | 国产午夜AAA片无码无片久久 | 无码人妻久久一区二区三区免费丨 | 国产挤奶水主播在线播放 | 国产精品户外野外 | 北条麻妃国产九九九精品视频 | 中文字幕狠狠 | JAPANESE熟女JAPANESEMA | 国产精品无码免费专区午夜 | 日批视频免费观看 | www.成人黄色 | 女人18毛片A级毛片嫰阝 | 男女啪激烈高潮喷水动态图 | 亚洲中文久久精品无码照片 | 免费人成无码视频在线观看 | 美女高潮无遮挡喷水视频 | 国产成人亚洲精品无码A大片 | 中文字幕乱偷无码动漫av | 国色天香社区视频在线 | 国产成人精品亚洲日本在线 | 人人看人人澡 | 欧美大香线蕉线伊人久久 | 丰满少妇高潮惨叫正在播放 | 色偷偷国色天香在线观看免费视频 | 亚洲欧美综合精品成人导航 | 正在播放的国产A一片 | 人妻校园激情另类 | 丰满熟妇人妻Av无码区 | 俺也去在线观看视频 | 中国毛片毛片 | 国产在线伊人 |