有些bash脚本写的不规范,没有在文件开头写#!,但是却能直接执行,可是如果看内核代码,shell脚本的加载函数中的开头就会判断,如果没有#!的话就会返回错误:static int load_script(struct linux_binprm *bprm,struct pt_regs *regs){... if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang)) return -ENOEXEC;...}为何在linux上写bash脚本时文件开头不写#!也能成功调用?bash中执行一条键盘命令时调用的是shell_execve函数,该函数封装了很多逻辑:int shell_execve (command, args, env){ execve (command, args, env); if (errno != ENOEXEC) { //非文件格式的错误,返回出错 } else { int larray = array_len (args) + 1; int i, should_exec = 0; int fd = open (command, O_RDONLY); if (fd != -1) { unsigned char sample[80]; //读取文件头的80个字节,然后进行格式判断 int sample_len = read (fd, &sample[0], 80); ...//如果是空文件,则正确返回,bash容忍这种情况 if (sample_len > 0 && sample[0] == '#' && sample[1] == '!') return (execute_shell_script(...));//执行shell脚本,其实现过程几乎和内核的script_format结构对象中的load_binary回调函数一模一样,主要用于不识别#!的操作系统 else if (check_binary_file (sample, sample_len)) //如果是二进制文件,则bash是不会逐行帮助执行的,出错返回 }...//bash会帮你执行command:/*args[0] = shell_name; //就是本shell的全路径args[1] = command; //要执行的脚本execve (shell_name, args, env); //重新执行*/}以下是上述shell_execve函数依赖的帮助函数和一些宏定义:由于bash是通过字符的可见性来判断是否是二进制文件的,由于空格/制表符等也是不可见的字符,然而它们确实是属于文本ascii码的,因此需要单独进行过滤:#define isspace(c) ((c) == ' ' || (c) == '/t' || (c) == '/n' || (c) == '/f')文本字符包括字母字符,数字字符以及除此之外的可以打印字符:#define isprint(c) (isletter(c) || digit(c) || ispunct(c))判断读取的80个字节是否全部是文本字符,只要有一个不是就将该文件判定为二进制文件,虽然前80字节的字符是文本不能保证后面的就一定是文本,可是bash毕竟需要做出一个权衡和一个假设:int check_binary_file (sample, sample_len){ for (i = 0; i < sample_len; i++) { if (sample[i] == '/n') break; if (!isspace (sample[i]) && !isprint (sample[i])) return (1); } return (0);}因此如果执行一个空文件,将会看到什么也不输出,如果执行一个非linux可执行的二进制文件比如cer格式的二进制证书,那么内核将返回ENOEXEC,接下来bash会尝试执行之,然后最终返回一个EX_BINARY_FILE错误,打印“cannot execute binary file”,如果执行一个文件头不是#!的文本文件,同样内核会返回一个ENOEXEC错误,但是bash却可以在shell_execve的后续执行中成功直接执行,可是如果在一个二进制文件的头部增加80字节或者80字节以上的文本,那么根据shell_execve的逻辑,bash也会将之作为脚本来分行执行,然后就会大错特错,因此虽然有的时候脚本中不写#!也能直接执行,但是要知道那可是bash本身帮你执行的,而不是内核直接执行的,起码执行绪会先从内核错误返回,然后继续shell_execve的后面的部分,确实稍微影响了效率,因此最好还是写上标准的#!,况且有的shell并不会像bash这么智能地重新解释执行命令。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271797