shell 是一个编程语言解释器,这个解释器解释从键盘输入的命令,也解释存储在脚本中的命令序列。
shell 脚本是一个包含一系列命令的文件。运行一个脚本就是运行这个文件中的每个命令。可以用一个 shell 脚本在一次请求中来执行多个命令。
1.sh 的编程特征:变量、 1/0 和 if.. then
shell 脚本是真正的程序。
脚本中除了命令之外还包括以下元素。
(1)变量
脚本中可以定义变量。在定义之后使用了它们,用前辍$来取得变量的值。变量名不一定要大写,只是习惯上将其大写。
(2) 用户输入
read 命令告诉 shell 要从标准输入中读入一个字符串。可以使用 read 来创建交互的脚本,也可以从文件或管道中读入数据。
(3) 控制
这个脚本包括了 if. .then. . else. .fi 控制语句。其他的脚本控制语句还有 while 、 case和for
(4) 环境
脚本使用一个名为 HOME 的变量。 HOME 的值是你的主目录的路径。 HOME 变量是由 login 程序设置的,可以被 login 进程的所有子进程使用。 HOME 变量是多个环境变量(environment variables) 中的一个。这些环境变量记录了个性化设置。而这些设置能影响很多程序的行为。比如, TZ 变量记录了当前的时区。将 TZ 设置为 "EST5EDT" 是告诉那些使用ctime 的程序,比如 date 或 1 日一 1 ,应该显示美国东部时间。
smsh.h
#include<stdio.h>
#define YES 1
#define NO 0
char * next_cmd(char *,FILE *);
char ** splitline(char *);
void freelist(char **);
void * emalloc(size_t);
void *erealloc(void *,size_t);
int execute(char **);
void fatal(char *,char *,int);
smsh1.c
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include"smsh.h"
#define DFL_PROMPT ">"
int main()
{
char * cmdline,*prompt,**arglist;
int result;
void setup();
prompt = (char *)DFL_PROMPT;
setup();
while((cmdline = next_cmd(prompt,stdin))!=NULL)//输入NULL退出shell
{
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);
}
splitline.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"smsh.h"
char * next_cmd(char *prompt,FILE *fp)//从输入流中读入下一个命令,调用malloc来分配内存以接受任意长度的命令行。碰到文件结束符,返回NULL
{
char *buf = NULL;
int bufspace = 0;
int pos = 0;//已经接受的字符个数
int c;
printf("%s",prompt);
while((c = getc(fp))!=EOF)//从标准输入一个一个接受字符
{
if(pos +1 >= bufspace)//接收到字符是否大于分配的内存
{
if(bufspace == 0)
buf = (char *)emalloc(BUFSIZ);//BUFSIZ == 8192
else
buf = (char *)erealloc(buf,(size_t)bufspace+BUFSIZ);//realloc(buf,bufspace+BUFSIZE);
bufspace += BUFSIZ;
}
if(c == '\n')
break;
buf[pos++] = (char)c;
}
if(c == EOF && pos == 0)//判断是否输入结束标志或者没输入
return NULL;
buf[pos] = '\0';
return buf;
}
#define is_delim(x) ((x) == ' '||(x) == '\t')
char ** splitline(char *line)
{
char *newstr(char *,int);
char ** args = NULL;
int spots = 0;//已分配的内存
int bufspace = 0;
int argnum = 0;
char *cp = line;//命令
char *start;
int len;
if(line == NULL)//判断输入命令是否为空
return NULL;
args = (char **)emalloc(BUFSIZ);
bufspace = BUFSIZ;
spots = BUFSIZ/sizeof(char *);//多少个char *
while(*cp!='\0')//判断输入命令格式 讲命令和参数分离
{
while(is_delim(*cp))//cp指向命令第一个字符
cp++;
if(*cp == '\0')
break;
if(argnum + 1 >= spots)//命令和参数的个数是否大于分配的内存
{
*args = (char *)erealloc(args,(size_t)bufspace+BUFSIZ);
bufspace += BUFSIZ;
spots += (BUFSIZ/sizeof(char *));
}
start = cp;//命令或者参数
len = 1;
while(*++cp!='\0'&&!(is_delim(*cp)))//命令的长度
len++;//命令、参数长度
args[argnum++] = newstr(start,len);
}
args[argnum]= NULL;
return args;
}
char * newstr(char *s,int l)
{
char *rv = (char *)emalloc((size_t)l+1);
rv[l] = '\0';
strncpy(rv,s,(size_t)l);//将命令拷贝到rv
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((char *)("out of memory"),(char*)(""),1);
return rv;
}
void * erealloc(void *p,size_t n)
{
void * rv;
if((rv = realloc(p,n)) == NULL)
fatal((char *)("realloc() failed"),(char *)(" "),1);
return rv;
}
execute.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
int execute(char *argv[])
{
int pid;
int child_info = -1;
if(argv[0] == NULL)
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 execut command");//执行失败报错
exit(1);
}
else
{
if(wait(&child_info) == -1)//等待子进程执行命令并退出
perror("wait");
}
return child_info;
}
Makefile
smsh1:smsh1.c splitline.c execute.c
gcc $^ -o $@ -g
diff命令用来比较两个文本文件,如果两个文件相同,diff返回0以表明成功。shell中以exit(0)表示成功。
shell命令中如果 if 后的条件是一系列的命令,那么最后一个命令的 exit 值被用作这个语句块的条件值,并由此来决定条件是否成立。
环境:
环境是每个程序都可以存取的→个字符串数组。每个数组中的字符串都以 var=value 这样的形式出现,数组的地址被存放在一个名为 envlron 的全局变量里。环境就是 envlron 指向的字符串数组,读环境就是读这个字符串数组,改变环境就是改变字符串、改变这个数组中的指针或者将这个全局指针指向其他数组。
exec清除所有数据:
对exec 的调用就像换脑,用目标程序的代码和数据替换调用程序的代码和数据。但是 envlron 指针指向的数组是惟一的例外,当内核执行系统调用
execve 时,它将数组和字符串复制到新的程序的数据空间, 子程序中环境的设置是父进程环境的复本,子进程不能修改父进程的环境。因为在进
程调用 fork 和 exec 时整个环境都被自动的复制了,所以通过环境来传递数据比较方便、
快捷。