學習理解shell的好辦法--編寫自己的shell 之二

來源:互聯網
上載者:User

標籤:http   os   檔案   io   art   cti   代碼   ar   

shell指令碼的最簡單形式就是一串命令的羅列,shell充當解譯器,一條條挨個執行,直到最後一個或遇到退出命令。但這隻能做很簡單的事情,只是省區了每次都要敲一邊命令的時間,要想完成更負責的功能,還要加上這些東西:
1.控制
前面的條件滿足了,然後幹什麼;不滿足,幹什麼。
2.變數
c=a+b, 用一種形式代表另一種形式,就是變數。因為形式不同了,就能用一種不變的表示另一種變化的。比如“程式設計語言”就可以當一個變數,可以賦值為“C語言”,“Perl語言”,“Lisp”語言等。變數還可用緩衝思想理解,一個杯子,可暫存一杯水,一杯果汁,一杯酒,而這三個名稱,又可歸為“液體”這個變數名
3.環境
其實白馬是屬於馬的,環境也是變數的一種。只不過它一般是由系統負責提供的,是為了免去軟體每次運行都要設定一些變數的麻煩,比如設定語言什麼的,有了環境就直接可以用這些變數了。常見的PATH,HOME. 命令env可看到你的系統的環境變數。

首先要做到工作是加入命令列解析,讓使用者可以在一行中輸入命令和參數。解析器就輸入的一行拆分成字串數組,傳給子進程的execvp。

講一些訊號的東西。訊號是由單個片語成的訊息,用於處理序間通訊,進程正在努力的跑著,你要和它說話,讓它退出、暫停,就得用訊號。kill -l可查看訊號列表。
進程處理訊號有三種情形,一是預設處理,通常是消亡,這個調用signal(SIGINT,SIG_DFL)恢複SIGINT的預設處理;二是忽略,通過這個調用signal(SIGINT,SIG_IGN);三是調用一個函數signal(signum,function);

2) SIGINT 程式終止(interrupt)訊號, 在使用者鍵入INTR字元(通常是Ctrl-C)時發出,用於通知前台進程組終止進程

3) SIGQUIT
和SIGINT類似, 但由QUIT字元(通常是Ctrl-\)來控制. 進程在因收到SIGQUIT退出時會產生core(吐核)檔案。

20) SIGTSTP 停止進程的運行, 但該訊號可以被處理和忽略. 使用者鍵入SUSP字元時(通常是Ctrl-Z)發出這個訊號
在這一版shell中,處理SIGINT和SIGQUIT,在shell中忽略它倆,但在子shell中恢複他們的預設操作,因為這樣可以在shell中要讓子進程的結束,而不至於把shell自己殺死。

shell的main函數如下:
/* chicken_sh.c * 破殼,小雞. * 增加了命令列處理,比egg_sh好用了 */#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>#include "smsh.h"#define DFL_PROMPT "> "void setup();void fatal(char *s1, char *s2, int n)int main(){  char *cmdline, *prompt, **arglist;  int result;    prompt = DFL_PROMPT;  setup();  while ((cmdline = next_cmd(prompt, stdin)) != NULL) {    if ((arglist = splitline(cmdline)) != NULL) {      result = execute(arglist);      freelist(arglist);    }    free(cmdline);  }  return 0;}void setup()/* 設定訊號處理函數 */{  signal(SIGINT, SIG_IGN);  signal(SIGQUIT, SIG_IGN);}void fatal(char *s1, char *s2, int n)/* 錯誤處理函數 */{  fprintf(stderr, "Error: %s, %s\n", s1, s2);  exit(n);}



函數解釋:
next_cmd  從輸入讀取下一個命令,用malloc分配記憶體以接受任意長度參數,碰到檔案結束,返回NULL
splitline     解析參數. 將一個字串分解為字串數組,並返回這個數組

chicken_sh 比較複雜些,所以代碼分成三個檔案 :chicken_sh.c, splitline.c, execute.c  .( 源碼下載)
這樣編譯:
gcc -o chichen_sh  chicken_sh.c splitline.c execute.c



其中的execute.c變化不大,只是增加了訊號處理

splitline.c有點複雜,需要說明一下,代碼如下:
/*splitline.c * 為chicken_sh讀取並解析命令 *char *next_cmd(char *prompt, FILE *fp)  取下一條指令 *char **splitline(char *str);            解析字串 */#include<stdio.h>#include<stdlib.h>#include<string.h>#include "chicken_sh.h"char *next_cmd(char *prompt, FILE * fp){    char *buf;    int bufspace = 0;    int pos=0;/* 當前位置 */    int c;    printf("%s", prompt);    while ((c = getc(fp)) != EOF) {/*若需要空間*/if (pos + 1 >= bufspace) {    if (bufspace == 0)buf = emalloc(BUFSIZ);    elsebuf = erealloc(buf, bufspace + BUFSIZ);/* 擴大分配的記憶體 */    bufspace += BUFSIZ;}/* 命令結束 */if (c == ‘\n‘)    break;/* 如果不結束,則添加進緩衝區 */buf[pos++] = c;    }    if (c == EOF && pos == 0)return NULL;    buf[pos] = ‘\0‘;    return buf;}#define is_delim(x) ((x) == ‘ ‘ || (x) == ‘\t‘)    /*參數分隔字元是空格或tab*/char *newstr(char *s, int l);char **splitline(char *line){    char **args;/*要返回的參數數組*/    int spots = 0;/*參數指標的容量*/    int bufspace = 0;/*緩衝空間*/    int argnum = 0;/*參數計數*/    char *cp = line;    char *start;    int len;    if (line == NULL)/* 什麼輸入也沒有 */return NULL;    args = emalloc(BUFSIZ);/* 分配參數數組 */    bufspace = BUFSIZ;    spots = BUFSIZ / sizeof(char *);    while (*cp != ‘\0‘) {while (is_delim(*cp))    cp++;if (*cp == "\0")    break;/* 確保參數數組的空間 */if (argnum + 1 >= spots) {    args = erealloc(args, bufspace + BUFSIZ);    bufspace += BUFSIZ;    spots += (BUFSIZ / sizeof(char *));}/* 標記開始的地方,尋找以\0 結束的位置 */start = cp;len = 1;while (*++cp != ‘\0‘ && !(is_delim(*cp)))    len++;args[argnum++] = newstr(start, len);    }    args[argnum] = NULL;    return args;}/* * 構造字串,以‘\0‘ 結尾*/char *newstr(char *s, int l){    char *rv = emalloc(l + 1);    rv[l] = ‘\0‘;    strncpy(rv, s, l);    return rv;}void freelist(char **list)/*參數用完後,釋放空間*/{    char **cp = list;    while (*cp)free(*cp++);    free(list);}void *emalloc(size_t n){    void *rv;    if ((rv = malloc(n)) == NULL)fatal("out of memory", "", 1);    return rv;}void *erealloc(void *p, size_t n){    void *rv;    if ((rv = realloc(p, n)) == NULL)fatal("realloc() failed", "", 1);    return rv;}



emalloc和erealloc為封裝上錯誤處理的分配空間的函數;
next_cmd接受使用者輸入的命令和參數並儲存在一個鏈表中;
splitline負責將接受的參數拆分成每個參數分開的一個指標數組,空間都是動態分配的, 因為使用者輸入命令時,各個參數是以空格或tab隔開的,newstr把他們拆分成字元獨立的字串,以‘\0‘結尾.

編譯後的chicken_sh, 運行起普通命令來和正常shell沒兩樣了,並且可以按ctrl+D退出。還可以做這幾個改進:
1.一行多個命令,用分號隔開
2.後台進程,即命令最後加上 "&"
3.可以用exit命令退出,這樣可以設定退出碼,像exit 1, exit 0

下面增加if..then控制語句
shell中的if和C中的if一個不同之處是,C中if作用在它後面一條語句或花括弧包住的語句塊,而shell是用then開始,用"fi"表示if的結束,這樣對於解釋啟動並執行程式流程設計來說是簡單的。
還有,if是基於命令以0退出表示成功的假設,比如:
if cat hello.cthenecho hellofi



如果cat命令執行成功,返回0,那麼then到fi的語句塊就會執行。注意,命令返回0和直接在if後面敲個0是不同的,如果想直接說明真假,可以用true和false, if true就相當與if後面跟了個執行成功的命令,false反之。

太長了,下一篇再寫...
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.