Unix環(huán)境下的shell腳本通常都是#!/bin/sh開頭,那么這句描述符究竟是什么含義呢窘拯,我試圖解答這個問題。
#!出現(xiàn)的位置
首先#!必須出現(xiàn)在shell腳本的開頭位置,后面跟一個shell解析器,比如/bin/sh拌倍,或者/bin/bash,或者/usr/bin/ksh, 等等.
看一個例子
$ cat a.sh
#!/bin/bash
readlink /proc/$$/exe
$ ./a.sh
/bin/bash
我們看到 a.sh使用的可執(zhí)行程序是/bin/bash噪径,這個/bin/bash是在a.sh文件里面通過#!指定的柱恤。
再看另一個例子a.out
$ cat a.c
#include <stdio.h>
int main()
{
printf("Hello a.out!\n");
return 0;
}
$ gcc t.c
$ ./a.out
Hello a.out!
$ bash ./a.out
./a.out: ./a.out: cannot execute binary file
a.out是一個可執(zhí)行程序(ELF格式),它可以獨自運行找爱,但是不能通過一個shell來運行梗顺。
分析
#!實際上就是文件的魔數(shù)(magic number)
我們知道ELF格式為文件頭4個字符是".ELF",即(0x 7f 45 4c 46)车摄,而其實字符"#!"是shell腳本文件的魔數(shù)荚守,即(0x 23 21),因為shell腳本是文本文件练般,#!就是兩個可讀的魔數(shù)字符。
這個魔數(shù)是干什么用的呢锈候?它是被操作系統(tǒng)exec系列函數(shù)使用的薄料,exec函數(shù)需要加載一個文件時,它會讀取文件開頭魔數(shù)域泵琳,如果是".ELF",那么就是一個ELF格式的可執(zhí)行文件乱凿,如果是"#!"那么就是一個腳本文件穷娱,然后再從"#!"后面繼續(xù)讀取腳本解析器,最后調(diào)用腳本解析器可執(zhí)行程序蛔垢,并把腳本本身作為參數(shù)傳遞給他。
注意
- 如果shell腳本的命令行中已經(jīng)有腳本解析器了迫悠,那么腳本中的"#!"內(nèi)容將被丟棄鹏漆,而采用命令行中的解析器。
例如
$ /bin/tcsh a.sh
/bin/tcsh
$ /usr/local/bin/pdksh a.sh
/usr/local/bin/pdksh
$ /bin/ksh a.sh
/bin/readlink
- 如果shell腳本中沒有魔數(shù)"#!"怎么辦
shell使用當(dāng)前的shell來處理创泄。
$ cat a.sh
readlink /proc/$$/exe
$ ./a.sh
/bin/bash
$ echo $SHELL
/bin/bash
- 對于一般文件的加載過程
$ ./file格式
當(dāng)前shell會讀取文件file的魔數(shù)
- 如果是".ELF"艺玲,二進(jìn)制可執(zhí)行程序,安裝ELF文件處理
- 區(qū)分.o, .so, etc.
- 如果是"#!"鞠抑,那么加載魔數(shù)后面指定的可執(zhí)行程序饭聚,并把file作為參數(shù)傳遞過去。
- 其它:file會作為一個當(dāng)前shell類型的腳本運行搁拙,即試圖把path_to_file的內(nèi)容按照腳本來解釋執(zhí)行秒梳。
- sh file格式
注意這種格式要求file必須是一個shell腳本,不能是二進(jìn)制可執(zhí)行程序箕速。
如果file是一個二進(jìn)制格式文件酪碘,則可以采用格式:sh -c file
對于后一種格式,其實不管file是一個腳本還是一個二進(jìn)制可執(zhí)行程序弧满,都可以運行婆跑。
我也不清楚為什么sh file格式不能支持file是二進(jìn)制可執(zhí)行程序,理論上sh還是可以去分析file的魔數(shù)庭呜,從而判斷file的類型滑进,然后做區(qū)分處理。