第九章:可编程的shell,shell变量和环境:编写自己的shell

0.摘要

概念与技巧
-Unix shell 是一种编程语言
-什么是shell脚本语言? shell如何处理脚本语言?
-shell如何处理结构化的工作?exit(0) = success
-为什么需要shell变量以及如何使用shell变量
-什么是环境?它是如何工作的?
相关的系统调用
-exit
-getenv
相关命令
-env

1.shell编程
#this is called script0
ls
echo the current date/time is
date
echo my name is 
whoami

以上就是一个简单的脚本.其中#开头表示注释,之后逐条执行知道shell的末尾或执行了exit.
sh + 脚本
当然可以添加脚本的访问方式,从而通过./来执行.

#! /bin/sh     
#script2: a real program with variables, input, and control flow     

BOOK=$HOME/gg    
echo find the $BOOK     
read NAME    
if grep $NAME $BOOK > $HOME/script.tmp    
then     
    echo Entries for $NAME     
    cat $HOME/script.tmp    
else     
    echo No Entries for $NAME     
fi     
exit 

其中$home/gouge里面是

gouge haha

脚本赋值之间不能出现空格,使用$来获取变量
脚本使用到的元素:
1.变量
其中BOOK和NAME是变量,习惯上大写,并且在引用变量前加上$
2.用户输入
read命令告诉shell从标准输入中读入一串字符串.可以使用read来创建交互式的脚本,也可以从文件或管道读入数据.

3.控制
if …then…else…fi 控制语句.当然还有while,case和for
4.环境
$HOME就是一个环境变量,表示当前登录用户的主目录.

2.smsh1-命令行解析

先给出代码再来解释

#include"cs_shell.h"

#define DEF_PROMPT ">"
int main()
{
    int status;
    char * cmdline, *prompt, **arglist;
    prompt = DEF_PROMPT;
    while((cmdline = next_cmd(prompt,stdin))!=NULL){  //get the input string
        if((arglist = splitline(cmdline))!=NULL)
        {
            status = 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);
}

以上需要注意的函数包括三个:
1.next_cmd:从输入流中读入下一个命令
2.splitline:将一个字符串分解成字符数组的形式,并且返回这个数组.调用malloc来分配内存.每个数组以NULL位结尾
3.execute:使用fork/execvp和wait来运行一个命令.execute返回命令的结束状态.

#include"cs_shell.h"

/*get next command and parse a string*/
char * next_cmd(char *prompt, FILE*fp)
{
    printf(" %s",prompt);
    int pos=0;       //the position of current
    int bufspace=0;  //the buffer space 
    int c;
    char *buf;  //the buffer
    while((c = getc(fp)) != EOF)
    {   
        if(pos+1>=bufspace) /*need more space*/
        {   
            if(bufspace ==0)
            {
                buf = emalloc(BUFSIZ); //new alloc the size of buf
            }else
            {
                buf = remalloc(buf,pos+BUFSIZ); //add size of buf 
            }
            bufspace +=BUFSIZ;      //add buffer size;
        }
        //end of command, the meaning the last string has the '\n' character
        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')

/*split a line into an array of strings*/
char ** splitline(char *line)
{
    char *cp;
    int len;        //the length of comand or the arguments
    char **args;
    int spots = 0;  /*spots in table*/
    int bufspace = 0; /*bytes in table*/
    cp = line;
    int argnum=0; //arguments numbers
    char *start; //set the start of the string 
    args = emalloc(BUFSIZ); //args size 
    bufspace = BUFSIZ;      //all the buffsize
    spots = BUFSIZ/sizeof(char*);   //the spots that can be calculated 
    while(cp!='\0')
    {   
        while(is_delim(*cp))   //the string start with th cp;
            cp++;
        if(*cp =='\0')
            break;
        /*make more room*/
        if(argnum+1 >= spots)
        {
            args = remalloc(args,bufspace+BUFSIZ);
            bufspace +=BUFSIZ;
            spots += (BUFSIZ/sizeof(char*));
        }
        /*mark the 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;
}
//copy the string from start 
char * newstr(char *start,int len)
{
    char *rv = emalloc(len+1);
    rv[len] = '\0';
    strncpy(rv,start,len);
    return rv;
}

void *emalloc(size_t n)
{
    void *rv;
    if((rv=malloc(n))==NULL)
        fatal("out of memory","",1);
    return rv;
}

void *remalloc(void *p,size_t n)
{
    void *rv;
    if((rv = realloc(p,n))==NULL)
        fatal("realloc() failed","",1);
    return rv;
}

void *freelist(char **arglist)
{
    char **cp = arglist;
    while(*cp)
        free(*cp++);
   free(arglist);
}

执行参数,创建子进程,执行argv中的命令以及参数,父进程等待子进程结束返回.

#include"cs_shell.h"
/*execute the pid*/
int execute(char *argv[])
{
    int status;
    int pid=fork();
    if(pid==-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(&status)==-1)
        {
            perror("wait");
        }
    }
    return status;
}
3.shell中的流程控制
3.1:if语句做些什么

通过执行if后面的语句,并且返回exit()的状态来判断接下来的执行路径.在Unix中,正确执行的返回值都为0. exit(0)表示执行成功.

3.2 if是如何工作的

1.shell运行if之后的命令
2.shell检查命令的exit状态
3.exit的状态为0意味着成功,非0意味着失败.
4.如果成功,shell执行then部分的代码
5.如果失败,shell执行else部分的代码
6.关键字fi标识if块的结束

3.3在smsh中增加if

1.增加一层:process

/*smsh2.c 添加了process来判断if流进程*/
#include"cs_shell.h"
#define DEF_PROMPT ">"
int main()
{
    int result;
    char * cmdline, *prompt, **arglist;
    prompt = DEF_PROMPT;
    while((cmdline = next_cmd(prompt,stdin))!=NULL){  //get the input string
        if((arglist = splitline(cmdline))!=NULL)
        {
            //status = execute(arglist);
           result = process(arglist); //切换成process
            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);
}

以上在main中将execute替换成process

#include"cs_shell.h"

int is_control_command(char *); 
int do_control_command(char **);
int ok_to_execute();

/*choose the execute road*/
int process(char **args)
{
    int rv; 
    if(args[0]==NULL)
        rv=0;
    else if(is_control_command(args[0]))
    {   
        do_control_command(args);
    }else if(ok_to_execute())
    {   
        rv = execute(args);
    }   
    return rv; 
}

在执行fork前控制命令之前判断控制命令行.
if then else fi
这四个部分有不同的操作。我们把它封装成process通过三个函数来处理区域问题。
(1)is_control_command:判断是否是控制命令,
(2)do_control_command:如果是控制命令,执行控制命令
(3)ok_to_execute:判断当前命令是否是可执行的,如果是,则直接执行.

以上三个函数的实现被封装在controlflow.c中:

/*controlflow.c*/
include"cs_shell.h"
int syn_err(char*);
enum states {NEUTRL,WANT_THEN,THEN_BLOCK};
enum results {SUCCESS,FAIL};

static int if_state = NEUTRL;
static int if_result = SUCCESS;
static int last_stat = 0;
/*determine the shell should be execute a command*/
int ok_to_execute()
{
    int rc =1; /*default is positive*/
    if(if_state == WANT_THEN)
    {   
        perror("want then cannot execute");
        rc =0; 
    }   
    else if(if_state ==THEN_BLOCK && if_result==SUCCESS)
    {   
        rc = 1;
    }else if(if_state == THEN_BLOCK &&if_result ==FAIL)
    {   
        rc = 0;
    }   
    return rc; 
}


/*boolean to report if the command is a shell control command*/
int is_control_command(char *s) 
{
    return (strcmp(s,"if")==0 || strcmp(s,"then")==0|| strcmp(s,"fi")==0);
}

int do_control_command(char **arg)
{
    char *s = arg[0];
    int rv = -1; //return value is -1
    if(strcmp(s,"if")==0)
    {   
        if(if_state !=NEUTRL)
        {           rv =0;
        }
    }else if(strcmp(s,"then")==0)
    {
        if(if_state!=WANT_THEN)
        {
            rv=syn_err("then unexpected");
        }else{
            if_state = THEN_BLOCK;
            rv = 0;
        }
    }else if(strcmp(s,"fi")==0)
    {
        if(if_state!=THEN_BLOCK)
        {
            rv = syn_err("fi error");
        }else{
            if_state = NEUTRL;
            rv=0;
        }
    }else
        fatal("international error processing:",s,2);
    return rv;
}


int syn_err(char *msg)
{
    if_state = NEUTRL;
    fprintf(stderr,"syntax error: %s\n",msg);
    return  -1;
}

            rv = syn_err("if unexpected");
        }else 
        {
            last_stat = process(arg+1); 
            if_result = (last_stat ==0?SUCCESS:FAIL);
            if_state = WANT_THEN;
           rv =0;
        }
    }else if(strcmp(s,"then")==0)
    {
        if(if_state!=WANT_THEN)
        {
            rv=syn_err("then unexpected");
        }else{
            if_state = THEN_BLOCK;
            rv = 0;
        }
    }else if(strcmp(s,"fi")==0)
    {
        if(if_state!=THEN_BLOCK)
        {
            rv = syn_err("fi error");
        }else{
            if_state = NEUTRL;
            rv=0;
        }
    }else
        fatal("international error processing:",s,2);
    return rv;
}


int syn_err(char *msg)
{
    if_state = NEUTRL;
    fprintf(stderr,"syntax error: %s\n",msg);
    return  -1;
}

得到输出结果,编译得到的smsh2,执行.

$ ./smsh2
>grep lp /etc/passwd
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
 >if grep lp /etc/passwd
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
 >then
 >echo ok
ok
 >fi
 >if grep ppj /etc/passwd
 >then  
 >echo ok
 >fi
 >then
syntax error: then unexpected
 >

存在的问题,标准执行是在读到shell的fi后,再执行整个if语句块.

4shell变量:局部和全局
4.1使用shell变量
操作类型语法注释
赋值var=value不能有空格
引用$var
删除unset var
输入read var也可以read var1 var2…
列出变量set
全局化export var

以上就是对于变量的一些操作
如何实现嵌套的if。

在shell中变量前面加上$来表示变量
shell中的变量
对于这些变量的操作:表9.5.1

5环境:个性化设置
5.1使用环境

1.列出环境
$env 命令可以列出所有的环境变量
2.更新环境变量
(1) var = value通过键值对的方法来更改
(2)export var可以把当前的var变量名改变成新的环境变量.
(3)在函数中,可以调用getenv()函数来得到环境变量的值

5.2什么是环境变量以及它怎么工作

环境变量是由environ的全局变量来维护的
每个程序都从调用它的进程中继承一个字符串列表,这个列表称为环境.环境用来保存会话(session)的全局设置和某个程序的参数设置,shell允许用户查看和修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值