编写自己的shell(2)

来源:互联网 发布:知之者 编辑:程序博客网 时间:2024/06/03 18:58

       脚本中除了命令之外还包括以下元素:变量,用户输入,控制流,环境变量。

上一次用fork,execvp,wait实现了一个能够创建进程和运行程序的shell。此次对这个shell做一些改进。加入命令行解析,这样用户可以在一行中输入命令和所有参数了,然后将控制语句if...then加入到这个shell中,最后加入局部变量和环境变量。本次先加入第一个功能。

      


       第一个改进是添加命令行解析的功能。用户可在一行中输入 比如: find /home -name core -mtime +3 -print

然后由解析器将命令行拆成字符串数组,以便传给execvp。在shell中忽略信号SIGINT和SIGQUIT,但是在子进程中恢复对信号SIGINT和SIGQUIT的默认操作,允许用户通过按表示结束文件的Ctrl-D来退出。此版本命名为smsh1.其shell的主函数如下:

int main(){char*cmdline, *prompt, **arglist;intresult;voidsetup();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;}
其中3个函数解释:

    1.next_cmd 从输入流读入下一个命令。它调用malloc来分配内存以接受任意长度的命令行。碰到文件结束符,它返回NULL。

    2.splitline 将一个字符串分解为字符串数组,并返回这个数组。它调用malloc来分配内存以接受任意参数个数的命令行。此数组以NULL标记结束。

    3.execute 运行一个命令,返回命令的结束状态。

  smsh1由三个文件组成  smsh1.c ,splitline.c,execute.c

   三者代码如下:

    smsh1.c:

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>#include"smsh.h"#defineDFL_PROMPT"> "int main(){char*cmdline, *prompt, **arglist;intresult;voidsetup();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()/* * purpose: initialize shell * returns: nothing. calls fatal() if trouble */{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);}

splitline.c:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include"smsh.h"char * next_cmd(char *prompt, FILE *fp){char*buf ; /* the buffer*/intbufspace = 0;/* total size*/intpos = 0;/* current position*/intc;/* input char*/printf("%s", prompt);/* prompt user*/while( ( c = getc(fp)) != EOF ) {/* need space? */if( pos+1 >= bufspace ){/* 1 for \0*/if ( bufspace == 0 )/* y: 1st time*/buf = emalloc(BUFSIZ);else/* or expand*/buf = erealloc(buf,bufspace+BUFSIZ);bufspace += BUFSIZ;/* update size*/}/* end of command? */if ( c == '\n' )break;/* no, add to buffer */buf[pos++] = c;}if ( c == EOF && pos == 0 )/* EOF and no input*/return NULL;/* say so*/buf[pos] = '\0';return buf;}#defineis_delim(x) ((x)==' '||(x)=='\t')char ** splitline(char *line){char*newstr();char**args ;intspots = 0;/* spots in table*/intbufspace = 0;/* bytes in table*/intargnum = 0;/* slots used*/char*cp = line;/* pos in string*/char*start;intlen;if ( line == NULL )/* handle special case*/return NULL;args     = emalloc(BUFSIZ);/* initialize array*/bufspace = BUFSIZ;spots    = BUFSIZ/sizeof(char *);while( *cp != '\0' ){while ( is_delim(*cp) )/* skip leading spaces*/cp++;if ( *cp == '\0' )/* quit at end-o-string*/break;/* make sure the array has room (+1 for NULL) */if ( argnum+1 >= spots ){args = erealloc(args,bufspace+BUFSIZ);bufspace += BUFSIZ;spots += (BUFSIZ/sizeof(char *));}/* mark start, then find end of word */start = cp;len   = 1;while (*++cp != '\0' && !(is_delim(*cp)) )len++;args[argnum++] = newstr(start, len);}args[argnum] = NULL;return args;}/* * purpose: constructor for strings * returns: a string, never NULL */char *newstr(char *s, int l){char *rv = emalloc(l+1);rv[l] = '\0';strncpy(rv, s, l);return rv;}void freelist(char **list)/* * purpose: free the list returned by splitline * returns: nothing *  action: free all strings in list and then free the 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;}


/* execute.c - code used by small shell to execute commands */#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>#include<sys/wait.h>int execute(char *argv[])/* * purpose: run a program passing it arguments * returns: status returned via wait, or -1 on error *  errors: -1 on fork() or wait() errors */{intpid ;intchild_info = -1;if ( argv[0] == NULL )/* nothing succeeds*/return 0;if ( (pid = fork())  == -1 )perror("fork");else if ( pid == 0 ){signal(SIGINT, SIG_DFL);signal(SIGQUIT, SIG_DFL);execvp(argv[0], argv);perror("cannot execute command");exit(1);}else {if ( wait(&child_info) == -1 )perror("wait");}return child_info;}

smsh.h:

#defineYES1#defineNO0char*next_cmd();char**splitline(char *);voidfreelist(char **);void*emalloc(size_t);void*erealloc(void *, size_t);intexecute(char **);voidfatal(char *, char *, int );intprocess();


编译运行:

$ gcc smsh1.c splitline.c execute.c -o smsh1

$ ./smsh1

>ps -f

UID        PID  PPID  C STIME TTY          TIME CMD
sk        3982  3972  0 19:49 pts/0    00:00:00 bash
sk        4637  3982  0 20:11 pts/0    00:00:00 ./smsh1
sk        4638  4637  0 20:11 pts/0    00:00:00 ps -f

>这里按Ctrl-D键

$

其中ps -f是./smsh1的子进程,./smsh1是bash的子进程.




0 0
原创粉丝点击