shebang

在Unix中,shebang其实就是指“#!”,它取自#(SHArp)和!(bang)。

它是很多脚本文件中第一行的前两个字符,用来告诉Unix系统要用shebang后面指定的解释器
来解释该脚本。所以,在很多脚本中,第一行往往都是这么写的:

! /abs/path/to/interpreter

根据wikipedia上的解释),shebang最初由
Dennis Ritchie引入的,时间大概是在Version 7和Version 8之间。也正是因为shebang是以#
开头,所以很多Unix上的脚本都是用#作为注释的开始。常见的有:

!/bin/sh

!/usr/bin/perl -w

!/bin/awk -f

!/usr/bin/env python

!/usr/bin/ruby

shebang没有你想象得这么简单。首先,它后面的解释器路径一般来说必须是绝对路径。(当然了,
有人也说像#!python这样的也能执行。)如果不是,可能就会出现类似的错误:


bash: ./foo.py: python: bad interpreter: No such file or directory

因为shell会直接用execve去执行这个文件,出错马上就退出。而execve会通过shebang辨认出这个一
个脚本文件,然后尝试用后面的解释器去执行,但它并没有在PATH中寻找解释器,而是完全依靠
你给出的路径。如果你不想指定绝对路径或者出于可移植的原因不好指定,那么你应该试试用env(1)
,就上面的python的shebang一样,它会帮你在PATH中搜索。python之所以更倾向于这个还有个原因,
就是env一般固定在/usr/bin目录下,而python的安装位置就相对不那么固定。

用env时你应该注意这么一个事实:传递给解释器的argv和你想象得并不一样。下面这个就是不对的:

!/usr/bin/env perl -w

shell会提示:/usr/bin/env: perl -w: No such file or directory
错误的根源就是perl -w被当成了整体传递给env。

用下面的程序来看一下参数传递过程:


/test.c/

include <stdio.h>

int main(int argc, char** argv) {
int i;
for (i=0; i<argc; i++)
fprintf(stdout, “argv[%d]: “%s”n”, i, argv[i]);
return 0;
}
然后把编译出的test当作解释器:


$ cat invoker.sh

!/home/wangcong/test -1 -2 -3

结果如下:


$ ./invoker.sh a b c
argv[0]: “/home/wangcong/test”
argv[1]: “-1 -2 -3”
argv[2]: “./invoker.sh”
argv[3]: “a”
argv[4]: “b”
argv[5]: “c”

当然了,并不是所有的Unix都是这样,但最起码Linux上的bash和zsh上就是如此。所以,要编写可移植
的脚本,你应该当心这一点!