Unix/Linux 的 shell
2015-07-29 12:59
573 查看
Unix中一个可执行程序是一个机器指令及其数据序列。进程是程序运行时的内存空间和设置。
shell即“壳”,是一个解释器。 一般作用是:
(1)运行程序
(2)管理输入输出
(3)可编程
一个shell的主循环执行下面的4步:
(1)用户键入可执行文件名
(2)shell建立一个新的进程来运行这个程序
(3)shell将程序从磁盘载入
(4)程序在进程中运行直到结束
第一部分:为了要写一最简单的shell,需要学会
(1)运行一个程序
(2)建立一个进程 fock()
(3)等待exit()
父进程如何等待子进程退出呢? 调用wait(&status)。这个过程是这样的,一个进程创建一个子进程,自己相应的为父进程。父进程调用wait后,父进程挂起直到子进程结束,子进程结束会调用exit(n) (n是0~255) 。然后唤醒父进程同时wait得到返回状态。
wait()函数
wait(int *statuspir) 参数statuspir是子进程的运行结果。 返回值 -1遇到错误;pid 结束进程的id。假如参数statuspir不为NULL,wait将退出状态或者信号序列存储到参数所指的整数中。
第二部分:shell脚本
编辑脚本
其中#! /bin/sh 是说此脚本用/bin/sh解释。#后面是注释,但#!是特殊符号,表示它后面的是解释此脚本的shell的路径。echo 是输出
执行此脚本用
脚本除了命令外还包括:
变量,用户输入,控制,环境。
我们的代码实现解释命令,变量,if-then 控制,环境。
以下是代码
文件smsh4.c
文件smsh.h
文件splitline.c
文件execute.c
文件process.c
文件builtin.c
文件varlib.c
文件varlib.h
好了我们编译下
参考Unix/Linux编程实践教程
down
shell即“壳”,是一个解释器。 一般作用是:
(1)运行程序
(2)管理输入输出
(3)可编程
一个shell的主循环执行下面的4步:
(1)用户键入可执行文件名
(2)shell建立一个新的进程来运行这个程序
(3)shell将程序从磁盘载入
(4)程序在进程中运行直到结束
第一部分:为了要写一最简单的shell,需要学会
(1)运行一个程序
(2)建立一个进程 fock()
(3)等待exit()
父进程如何等待子进程退出呢? 调用wait(&status)。这个过程是这样的,一个进程创建一个子进程,自己相应的为父进程。父进程调用wait后,父进程挂起直到子进程结束,子进程结束会调用exit(n) (n是0~255) 。然后唤醒父进程同时wait得到返回状态。
wait()函数
wait(int *statuspir) 参数statuspir是子进程的运行结果。 返回值 -1遇到错误;pid 结束进程的id。假如参数statuspir不为NULL,wait将退出状态或者信号序列存储到参数所指的整数中。
第二部分:shell脚本
vi time.sh
编辑脚本
#! /bin/sh #date server echo urrnet date is date echo my name is whoami
其中#! /bin/sh 是说此脚本用/bin/sh解释。#后面是注释,但#!是特殊符号,表示它后面的是解释此脚本的shell的路径。echo 是输出
执行此脚本用
sh time.sh
脚本除了命令外还包括:
变量,用户输入,控制,环境。
我们的代码实现解释命令,变量,if-then 控制,环境。
以下是代码
文件smsh4.c
/** * smsh4.c small-shell version 4 * small shell that supportscommand line parsing * and if ..then .. else .fi logic(by calling process() ) * * build: gcc -o smsh4 smsh4.c splitline.c execute.c process.c\ * controflow.c builtin.c varlib.c */ #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<signal.h> #include<sys/wait.h> #include "smsh.h" #define DFL_PROMPT "> " void setup(void); int main(void) { char *cmdline, *prompt, **arglist; int result; prompt = DFL_PROMPT; setup(); while((cmdline = next_cmd(prompt, stdin)) != NULL) { if((arglist = splitline(cmdline)) != NULL) { result = process(arglist); freelist(arglist); } free(cmdline); } return 0; } /** * purpose: initialize shell * returns: nothing. calls fatal() if trouble */ void setup(void) { extern char **environ; VLenviron2table(environ); 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); }
文件smsh.h
#ifndef _SMSH_H #define _SMSH_H #define YES 1 #define NO 0 char *next_cmd(char *prompt, FILE *fp); char **splitline(char *line); void freelist(char **list); void *emalloc(size_t n); void *erealloc(void *p, size_t n); int execute(char **argv); void fatal(char *s1, char *s2, int n); int ok_to_execute(void); int builtin_command(char **args, int *resultp); #endif
文件splitline.c
/** * splitline.c * command reading and parsing functions for smsh * * char *next_cmd(char *prompt, FILE *fp); - get next command * char **splitline(char *str); - parse a string */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include "smsh.h" /** * purpose: read next command line fp * return : dynamically allocated string holding command line * errors : NULL at EOF (not really an error) * call fatal from emalloc() * notes: allocates space in BUFSIZ chunks */ char *next_cmd(char *prompt, FILE *fp) { char *buf; // the buffer int bufspace = 0; // total size int pos = 0; // current position int c; // input char printf("%s", prompt); while((c = getc(fp)) != EOF) { /* need space? */ if(pos + 1 >= bufspace) // 1 for \0 { if(bufspace == 0) // y: 1st time { buf = emalloc(BUFSIZ); } else { buf = erealloc(buf, bufspace + BUFSIZ); } bufspace += BUFSIZ; // update size } if( c == '\n') // end of command? { break; } buf[pos++] = c; // no, add to buffer } if(c == EOF && pos == 0) // EOF and no input { return NULL; } buf[pos] = '\0'; return buf; } #define is_delim(x) ((x) == ' '|| (x) == '\t') /** * purpose: split a line into array of white-space tokens * returns: a NULL-terminated array of pointers to copies of * the tokens or NULL if line if no tokens in the line * action : traverse the array , locate strings, make copies * note : strtok() could work, but we may want to add quotes later */ char **splitline(char *line) { char *newstr(); char **args; int spots = 0; // spots in table int bufspace = 0; // bytes in table int argnum = 0; // slots used char *cp = line; // pos in string char *start; int len; if(line == NULL) // handle spacial 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, nuver NULL */ char *newstr(char *s, int len) { char *rv = emalloc(len + 1); rv[len] = '\0'; strncpy(rv, s, len); return rv; } /** * purpose: free the list returned by spitline * returns: nothing * action: free all strings in list and then free the list */ 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; }
文件execute.c
/** * 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> #include "varlib.h" /** * purpose: run a program passing it arguments * return: status returned via wait, or -1 on error * errors: -1 on fork() or wait() errors */ int execute(char **argv) { extern char **environ; int pid; int child_info = -1; if(argv[0] == NULL) // nothing succeeds { return 0; } if((pid = fork()) == -1) { perror("fork"); } else if(pid == 0) { environ = VLtable2environ(); // new line signal(SIGIN fcfb T, 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; }
文件process.c
/** * process.c * command processing layer * * The process(char **arglist) function is called by the main loop * It sits in front of the execute() function.The layer handles * two main classes of processing: * a) built -in functions (e.g exit(), set, = , read, ..) * b) control structures(e.g. if, while, for) */ #include<stdio.h> #include "smsh.h" int is_control_command(char *s); int do_control_command(char **args); int ok_to_execute(void); /** * purpose: process user command * returns: result of processing command * details: if a built - in then call appropriate function, * if not, execute() * errors: arise from subroutines, handled there */ int process(char **args) { int rv = 0; if(args[0] == NULL) { rv = 0; } else if( is_control_command(args[0]) ) { rv = do_control_command(args); } else if(ok_to_execute() ) { if( ! builtin_command(args, &rv)) { rv = execute(args); } } return rv; }
/** * "if" processing is done with two state variables * if_state and if_result */ #include<stdio.h> #include "smsh.h" enum states{NEUTRAL, WANT_THEN, THEN_BLOCK}; enum results{SUCCESS, FALL}; static int if_state = NEUTRAL; static int if_result = SUCCESS; static int last_stat = 0; int syn_err(char *msg); /** * purpose: determine the shell should execute a command * returns: 1 for yes, 0 for no * details: if in THEN_BLOCK and if_result was SUCCESS the yes * if in THEN_BLOCK and if_result was FALL then no * if in WANT_THEN then syntax error(sh is different) */ int ok_to_execute(void) { int rv = 1; // default is positive if(if_state == WANT_THEN) { syn_err("then expected"); rv = 0; } else if(if_state == THEN_BLOCK && if_result == SUCCESS) { rv = 1; } else if(if_state == THEN_BLOCK && if_result == FALL) { rv = 0; } return rv; } /** * purpose: boolean to report if the command is a shell control command * returns: 0 or 1; */ int is_control_command(char *s) { return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 || strcmp(s, "fi") == 0); } /** * purpose: Process "if", "then", "fi" - change state or detect error * returns: 0 if ok, -1 for syntax error */ int do_control_command(char **args) { char *cmd = args[0]; int rv = -1; if(strcmp(cmd, "if") == 0) { if(if_state != NEUTRAL) { rv = syn_err("if unexpected"); } else { last_stat = process(args + 1); if_result = (last_stat == 0) ? SUCCESS : FALL; if_state = WANT_THEN; rv = 0; } } else if(strcmp(cmd, "then") == 0) { if(if_state != WANT_THEN) { rv = syn_err("then unexpeccted"); } else { if_state = THEN_BLOCK; rv = 0; } } else if(strcmp(cmd, "fi") == 0) { if(if_state != THEN_BLOCK) { rv = syn_err("fi unexpected"); } else { if_state = NEUTRAL; rv = 0; } } else { fatal("internal error processing:", cmd, 2); } return rv; } /** * purpose: handles syntax errors in control structures * details: resets state to NEUTRAL * returns: -1 in interactive. Should call fatal in scripts */ int syn_err(char *msg) { if_state = NEUTRAL; fprintf(stderr, "syntax error: %s\n", msg); return -1; }
文件builtin.c
/** * builtin.c * contains the switch and the function for builtin command */ #include<stdio.h> #include<string.h> #include<ctype.h> #include"smsh.h" #include"varlib.h" int assign(char *); int akname(char *); /** * purpose: run a bulitin command * returns: 1 if args[0] is builtin, 0 if not * details: test args[0] agsinst all known built ins. * Call functions */ int builtin_command(char **args, int *resultp) { int rv = 0; if(strcmp(args[0], "set") == 0) { VLlist(); *resultp = 0; rv = 1; } else if(strchr(args[0], '=') != NULL) { *resultp = assign(args[0]); if(*resultp != -1) { rv = 1; } } else if(strcmp(args[0], "export") == 0) { if(args[1] != NULL && okname(args[1])) { *resultp = VLexport(args[1]); } else { *resultp = 1; } rv = 1; } return rv; } /** * purpose: execute name = val AND ensure that name is legal * returns: -1 for illegal lval, or result of VLstore * waring: modifies the string, but retores it to normal */ int assign(char *str) { char *cp; int rv; cp = strchr(str, '='); *cp = '\0'; rv = okname(str) ? VLstore(str, cp + 1) : -1; *cp = '='; return rv; } /** * purpose: determines if a string is a legal variable name * returns: 0 for no, 1 for yes */ int okname(char *str) { char *cp; for(cp = str; *cp != '\0'; cp++) { if( (isdigit(*cp) && cp == str) || !(isalnum(*cp)) || *cp == '_') { return 0; } } return (cp != str); // no empty strings, either }
文件varlib.c
/** * varlib.c * a simple storage to store name = value paris * with facility to mark items as part of the environment * * interface: * VLstore(name, value) returns 1 for ok, 0 for no * VLlookup(name) returns string or NULL not there * VLlist() prints out current table * * environment-related function * VLexport(name) adds name to list of env vars * VLtable2environ() copy from table to environ * VLenviron2table() copy from environ to table * */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include"varlib.h" static struct var tab[MAXVARS]; /* private methods */ static char *new_string(char * name, char *val); static struct var *find_item(char *name, int first_blank); /** * traverse list, if found, repalce it, else add at end * since there is no delete, a blank one is a free one * return 1 if trouble, 0 if ok (like a command) */ int VLstore(char *name, char *val) { struct var *itemp; char *s; int rv = 1; /* find spot to put it and make new string */ if( (itemp = find_item(name, 1)) != NULL && (s = new_string(name, val)) != NULL) { if(itemp->str) // bas a val? { free(itemp->str); // y: remove it } itemp->str = s; rv = 0; // ok } return rv; } /** * returns new string of form name = value or NULL on error */ static char *new_string(char *name, char *val) { char *retval; retval = (char *)malloc(strlen(name) + strlen(val) + 2); if(retval != NULL) { sprintf(retval, "%s=%s", name, val); } return retval; } /** * returns value of var or empty string if not there */ char *VLlookup(char *name) { struct var *itemp; if( (itemp = find_item(name, 0)) != NULL ) { return (itemp->str + 1 + strlen(name) ); } return ""; } /** * marks a var for export, adds it if not there * returns 1 for no, 0 for ok */ int VLexport(char *name) { struct var *itemp; int rv = 1; if( (itemp = find_item(name, 0)) != NULL) { itemp->global = 1; rv = 0; } else if(VLstore(name, "") == 1) { rv = VLexport(name); } return rv; } /** * searches table for an item * returns ptr to struct or NULL if not found * */ static struct var *find_item(char *name, int first_blank) { int i; int len = strlen(name); char *s; for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i) { s = tab[i].str; if(strncmp(s, name, len) == 0 && s[len] == '=') { return &tab[i]; } } if(i < MAXVARS && first_blank) { return &tab[i]; } return NULL; } /** * performs the shell's set command * Lists the contens of the varible table, marking each * exported variable with the symble '*' */ void VLlist(void) { int i; for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i) { if(tab[i].global) { printf("* %s\n", tab[i].str); } else { printf(" %s\n", tab[i].str); } } } /** * initialize the variable table by loading array of strings * return 1 for ok, 0 for not ok */ int VLenviron2table(char *env[]) { int i; char *newstring; for(i = 0; env[i] != NULL; ++i) { if(i == MAXVARS) { return 0; } newstring = (char *)malloc(1 + strlen(env[i])); if(newstring == NULL) { return 0; } strcpy(newstring, env[i]); tab[i].str = newstring; tab[i].global = 1; } while(i < MAXVARS) // we don't need this { tab[i].str = NULL; // static globals are nummed tab[i++].global = 0; // by default } return 1; } /** * build an array of pointers suitable for making a new environment note, * you need to free() this when done to avoid memory leaks */ char **VLtable2environ(void) { int i; int j; int n; char **envtab; // array of pointers /* first, count the number of globle variables */ for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i) { if(tab[i].global == 1) { n++; } } /* then, allocate space for that many variable */ envtab = (char **)malloc( (n + 1) * sizeof(char *) ); if(envtab == NULL) { return NULL; } /* then, load the array with pointers */ for(i = 0, j = 0; i < MAXVARS && tab[i].str != NULL; ++i) { if(tab[i].global == 1) { envtab[j++] = tab[i].str; } } envtab[j] = NULL; return envtab; }
文件varlib.h
/** * varlib.h */ #ifndef _VARLIB_H #define _VARLIB_H #define MAXVARS 200 // a linked list would be nicer struct var { char *str; // name = val string int global; // a boolean }; int VLstore(char *name, char *val); char *VLlookup(char *name); int VLexport(char *name); void VLlist(void); int VLenviron2table(char **env); char **VLtable2environ(void); #endif
好了我们编译下
gcc -o smsh4 smsh4.c splitline.c execute.c process.c controflow.c builtin.c varlib.c
参考Unix/Linux编程实践教程
down
相关文章推荐
- android wifi 无线调试
- 运维入门
- 动态清空 nohup 输出文件
- install scrapy with pip and easy_install
- Linux VS Unix:Linux欲一统天下 Unix不死
- Linux Shell常用技巧
- shell字符串操作详解
- Shell中删除某些文件外所有文件的3个方法
- Ruby中执行Linux shell命令的六种方法详解
- VB使用shell函数打开外部exe程序的实现方法
- Shell编程的一些知识集合
- Shell中的for和while循环详细总结
- 什么是Shell?Shell脚本基础知识详细介绍
- Shell脚本中引用、调用另一个脚本文件的2种方法
- Shell脚本解压rpm软件包
- Shell脚本实现复制文件到多台服务器的代码分享
- Shell脚本实现批量替换文件内容
- Shell脚本实现的一个简易Web服务器例子分享
- linux Shell学习笔记第五天
- Perl 获取shell命令的执行结果