在脚本文件的第 1 行往往会看到:这里 bash 就是我们要求用来解析当前脚本的脚本解释器,对脚本解释器的调用由内核使调用 exec 函数的进程来执行。下面程序使用 exec 函数来模拟脚本文件被执行的过程。
程序代码如下:
[C++] 纯文本查看 复制代码 #include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
void err_msg (const char *msg)
{
perror (msg);
exit (EXIT_FAILURE);
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0) {
err_msg ("fork error");
} else if (pid == 0) {
if (execl("/home/beyes/shell/temp.sh", "temp.sh", "arg1", "arg2", (char *)0) < 0)
err_msg("execl error");
}
if (waitpid(pid, NULL, 0) < 0)
err_msg("waitpid error");
exit (EXIT_SUCCESS);
}
在上面程序中,我们使用 execl() 函数来执行位于 /home/beyes/shell/ 下的 temp.sh 脚本文件,并向该脚本文件传递了两个命令行参数 arg1 和 arg2 。
temp.sh 文件的内容如下:
[Bash shell] 纯文本查看 复制代码 #!/home/beyes/bin/tinyshell -x
echo hello tinyshell
echo hello execl
date
在上面的脚本中,自定义了一个名为 tinyshell 的脚本解析程序,后面还给它加了个 -x 参数 (在 bash 里,-x 参数是用来辅助调试用的);这里,并不打算处理这个 -x 参数以及传递进来的 arg1 和 arg2 这两个命令行参数。
execl() 要求解析该脚本文件,当 execl() 执行后,内核会将此任务交给这个脚本文件第一行所指定的解析器 tinyshell 来完成。这个简单的脚本解析器实现如下:
[C++] 纯文本查看 复制代码 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
FILE *fp;
char combuf[512];
char *pbuf = combuf;
int c;
for (i = 0; i < argc; i++)
printf ("argv[%d] = %s\n", i, argv[i]);
printf ("------------interpret script begin------------\n");
fp = fopen(argv[2], "r");
if ((unsigned char)fgetc(fp) == '#') {
while ((unsigned char)(c = fgetc(fp)) != '\n')
continue;
}
while (!feof(fp)) {
fseek (fp, -1, SEEK_CUR);
while ((unsigned char)(c = fgetc(fp)) == '\n')
continue;
fseek (fp, -1, SEEK_CUR);
while ((unsigned char)(c = fgetc(fp)) != '\n')
*pbuf++ = (unsigned char)c;
*pbuf = '\0';
system(combuf);
pbuf = combuf;
fgetc(fp);
}
fclose(fp);
return 0;
}
说明:
一开始将命令行参数打印出来,然后下面主要利用 system() 函数来执行这些命令。
需要注意的是:
由于 fgetc() 返回的是一个由 unsigned char 类型扩展的整型值,由于我们这里读取的都是普通的字符类型,所以在返回值前面我们也用 unsigned char 进行了类型的转换。
在 23 行的 if 判断里,主要是去掉第一行内容,即 #!/home/beyes/bin/tinyshell -x ,这一行内容不是命令,当然也无需执行。
接下来,用 feof(fp) 函数一直跟踪整个命令内容直到文件末尾,feof() 函数是通过判断文件流指针的当前位置来判断是否已达文件末尾的。关于 feof() 函数可参考:http://www.groad.net/bbs/read.php?tid-931.html
在
[C++] 纯文本查看 复制代码
while ((unsigned char)(c = fgetc(fp)) == '\n')
continue;
里,目的是过滤掉脚本中每一行命令间的杭欢空白。
fseek() 函数用来调整文件流指针的位置,这里往前挪 1 个字节,目的是正确读取到命令,否则可能会漏掉命令字符串,比如读取到 echo 命令时,会被读到 cho 而漏掉第一个字母 e 。
fseek() 函数的详细讨论可参考:http://www.groad.net/bbs/read.php?tid-566.html
在 pbuf = combuf; 这条语句下的 fgetc(fp); 是用来读取可能的 EOF 字符。如果还没遇到 EOF ,那么再一次 while 循环开始时也要用 fseek() 来调整一下指针位置,否则也有可能造成字符读取遗漏。
运行输出:beyes@debian:~/C/misc$ ./execsh
argv[0] = /home/beyes/bin/tinyshell
argv[1] = -x
argv[2] = /home/beyes/shell/temp.sh
argv[3] = arg1
argv[4] = arg2
------------interpret script begin------------
hello tinyshell
hello execl
Wed Jun 15 04:19:35 EDT 2011 这只是一个相当简单的且简陋的脚本解析器,通过它演示了 shell 脚本是如何被解释运行的。 |