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允许用户查看和修改