Shell是如何识别命令与参数的

不管我们用的是Ubuntu还是其它的发行版,尽管图形界面日新月异,但Shell总是我们得力的好伙伴!可是Shell是如何识别命令与参数的,它的算法是怎样实现的呢?初学linux编程的Maxiee今天在书上刚刚学到。于是记录一下自己的心得。

如果要在Shell中运行一个命令,我们只需要输入它的名字,随后附上相应的参数,回车便可执行,随后程序运行的结果就得出了,就像下图这样:

linux下的shell

我们在提示符$后输入了:“ls /home/maxiee/code”,然后按下回车,就得到了我们想要的结果,在本例中,是看看Maxiee存放代码的文件夹里都有什么东西。

我们输入的内容由两部分组成。第一部分是命令,在本例中是“ls”;第二部分是参数,在本例中是“/home/maxiee/code”。当然,在一些复杂的指令里,会有不止一个参数。

知道了这些,下面我们探讨这样几个问题:

1. 读取输入

我们首先要把用户输入的“ls /home/maxiee/code”都成功读取到,放进一个数组中。

在这里,采用的策略是,不停地获取一个字符,只要它不是回车,就接到我们的数组末尾。stdin 就代表用户的输入,getc(stdin) 就可以实现读取用户输入的一个字符。而这个数组是我们用malloc动态申请的,还要考虑到,如果它长度不够的的话,还要remalloc加以扩大。

这个功能函数代码附在最末,见【附】代码1

2. 分成什么样?

我们首先要明确的是,获得了这样一个数组,我们要把它变换、切分成另外一种形式,在一个子进程里,传送给一个函数,通过这个函数,来执行指令。

这个函数就是execvp。调用语句是:execvp(argv[0],argv);

这里的这个数组argv,就是我们要变换成的形式。对于它的内容和理解,可以参考Maxiee之前的笔记linux下more指令代码注释。其实,我们记得main函数不是可以带两个参数嘛,第二个参数av[]就是了!

所以,我们需要的这个数组argv,应该是这个样子的:

argv

3. 如何分?

首先我们要对保存着“ls /home/maxiee/code” 的数组,从头开始扫描。我们按照空格加以区分,那个这个字符串就可以被分成一个一个的单独部分,我们设立两个指针,一个指头部,一个指尾部,由此就获得了这个部分的长度;而有个头部有了长度,就能轻松地将这个部分拷贝出来。拷贝到哪里呢?当然是赶快申请内存,塞到上图所示的小“抽屉”里!

这部分的代码及注释附在最末,见【附】代码2 。

4. 【附】代码1

[c]

char *next_cmd(char * prompt,FILE *fp)
{
char *buf;
int bufspace = 0;
int pos = 0;
int c;

printf("%s",prompt);
while( (c=getc(fp))!=EOF){
if(pos+1>=bufspace){
if(bufspace == 0)
buf = emalloc(BUFSIZ);
else
buf = erealloc(buf,bufspace + BUFSIZ);
bufspace += BUFSIZ;
}
if(c == 'n')
break;
buf[pos++] =c;
}
if(c==EOF&&pos==0)
return NULL;
buf[pos]='\0';
return buf;
}

[/c]

5. 【附】代码2

[c]

//判断是不是空格或者TAB
#define is_delim(x) ((x)==' '||(x)=='t')

char ** splitline(char line)
{
char *newstr();
//这两个
代表什么?指针的指针
//指针数组?二维矩阵?
char **args;
int spots = 0;
int bufspace =0;
int argnum = 0;
//保存了要分的命令、参数
char *cp = line;
char *start;
int len;

//用户只按回车,却没输入命令
if(line==NULL)
return NULL;
//
args = emalloc(BUFSIZ);
//缓冲的大小
bufspace =BUFSIZ;
//BUFSIZ存了多少个指针?
spots = BUFSIZ/sizeof(char *);

while(cp!='\0')
{
//如果有空格或TAB
while(is_delim(
cp))
//跳过,前进到下一个参数
cp++;

//如果到头了
if(*cp == "\0")
break;

//args中用一个指针来代表参数、命令
//如果指针数超过我们申请的
//重新在分配,再来一发这么大的!
if(argnum+1>=spots){
args = erealloc(args,bufspace+BUFSIZ);
bufspace += BUFSIZ;
spots += (BUFSIZ/sizeof(char *));
}

//把指针又传了一次
//现在start也代表命令行了
start =cp;
len =1;

//没到头也不是空格间断
//那么就是命令或参数
//用来获得命令或者参数的长度
while(++cp!='\0'&&!(is_delim(cp)))
len++;
//将这个命令、参数装入args数组
args[argnum++] = newstr(start,len);
}
args[argnum] = NULL;
return args;
}

char *newstr(char *s,int l)
{
char *rv = emalloc(l+1);
rv[l]='\0';
strncpy(rv,s,l);
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("out of memory","",1);
return rv;
}

void *erealloc(void *p,size_t n)
{
void *rv;
//realloc重新分配所申请的内存大小
//如果原来的不够了,可以重新分配足够的!
if( (rv=realloc(p,n))==NULL)
fatal("realloc() failed","",1);
return rv;
}

[/c]