解释器、解释器文件

1. 什么是解释器文件

解释器文件是文本文件,它起始行的格式为:

#! pathname [optional-argument]

感叹号和pathname之间的空格是可选的。常见的解释器文件:

#!/bin/sh
...

pathname通常是绝对路径,不使用$PATH进行路径搜索。对解释器文件的识别通常由内核作为exec系统调用的一部分来完成。内核使得调用exec函数的进程所要执行的程序不是该解释器文件本身,而是该解释器文件第一行中pathname所指定的文件。

就是说,如果execlp或者execvp使用路径前缀中的一个找到了一个可执行文件,但是该文件不是由链接编辑器产生的可执行文件(二进制可执行文件),(如果该文件的第一行没有按照上面的方式指出pathname等)则认为该文件是一个shell脚本,试着调用/bin/sh,并以filename作为shell输入。

注意以下区别,

  •       解释器文件: 以#!开头的文本文件
  •       解释器:解释器文件第一行pathname所指定的程序

解释器文件的第一行有一定的长度限制,例如Linux为127字节。

2. 命令行参数

假设解释器文件/dir/foo.sh的内容为,

#!/bin/sh param1
...
     
      <1>     <2>

而调用execl执行这个解释器文件的C代码为,

      execl("/dir/foo.sh", "foo.sh", "myarg1", "MY ARG2", NULL);
  
                <A>           <B>      <C>       <D>
                             argv[0]  argv[1]   argv[2]

那么,新程序(/bin/sh)中的实际的参数分别为:

argv[0]: /bin/sh        <1>
argv[1]: param1         <2>
argv[2]: /dir/foo.sh    <A>
argv[3]: myarg1         <C>
argv[4]: MY ARG2        <D>

除了<1>和<2>,内核用execl的pathname(<A>)代替了原来的argv[0](<B>),这是因为pathname一般含更有用的信息。

3. 实例

有这样一个解释器文件

#!/bin/awk -f
BEGIN {
    for (i = 0; i < ARGC; i++)
        printf("ARGV[%d] = %s\n", i, ARGV[i])
    exit
}

如果在命令行中执行“awkexample file1 FILENAME2 f3”,结果如下:

$ awkexample file1 FILENAME2 f3
ARGV[0] = awk
ARGV[1] = file1
ARGV[2] = FILENAME2
ARGV[3] = f3

那么Shell会这样调用exec,

exec("/usr/local/bin/awkexample",
        "awkexample", "file1", "FILENAME2", "f3", NULL);

于是新程序的命令行参数相当于,

/bin/awk -f /usr/local/bin/awkexample file1 FILENAME2 f3

4. 为什么使用解释器文件

解释器文件能得到效率方面的好处,但代价是内核的额外开销(因为识别解释器文件的是内核)。以下是使用解释器文件的理由:

  • 有些程序是使用某种脚本语言编写的脚本,而解释器文件隐藏了这一事实。
  • 解释器文件使我们可以使用除/bin/sh以外的shell来执行shell脚本。

5. 注意事项

  • #!这一行的长度有一定的限制,linux为127,其他的系统最少的为63。
  • 别在选项之后放任何空白,空白也会跟着选项一起传给被引用的程序。
  • 解释器需要完整路径名。以避免可移植问题。

6. 参考资料

<apue> [8.10 exec函数];[8.12 解释器文件]
<sed & awk> [10.9 Invoking awk Using the #! Syntax]
<shell脚本学习指南> [2.4 自给自足的脚本:位于第一行的#!]

posted @ 2012-09-16 17:22  beacer  阅读(2353)  评论(0编辑  收藏  举报