没有#!的bash脚本的执行

简介:
有些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

相关文章
|
6月前
|
监控 Shell Linux
使用Python和Bash编写内网监控工具:自动巡检脚本示例
为了确保内网的顺畅运行,自动化监控工具变得不可或缺。本文将介绍如何使用Python和Bash编写一个简单而强大的内网监控工具,它可以帮助您自动巡检网络和系统状态,及时发现问题并采取措施。
246 0
|
8月前
|
Shell
使用Bash备份脚本
使用Bash备份脚本
54 1
|
10月前
|
运维 Shell
善用chatGPT学习 | bash脚本如何判断字符串在数组中
善用chatGPT学习 | bash脚本如何判断字符串在数组中
147 0
|
11月前
|
安全 Shell 开发工具
记一次符合Google Coding Style的Bash脚本重构
记一次符合Google Coding Style的Bash脚本重构。最近我在思考这样一个问题,顺便看一下gpt对这个问题的解释。搜索发现:
记一次符合Google Coding Style的Bash脚本重构
|
11月前
|
Shell Linux 程序员
Shell-/bin/bash和/bin/sh解释器的误用引起的脚本语法错误
Shell-/bin/bash和/bin/sh解释器的误用引起的脚本语法错误
282 0
|
Ubuntu Shell Linux
Shell脚本的常用执行方式、bash 和 sh 的关系、子shell、Centos 默认的解析器是 bash、Linux 提供的 Shell 解析器、Shell 概述、Shell 脚本入门
采用 bash 或 sh+脚本的相对路径或绝对路径(不用赋予脚本+x 权限)、采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x)、在脚本的路径前加上“.”或者 source(了解)原因: 前两种方式都是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则 子 shell 关闭,回到父 shell 中。第三种,也就是使用在脚本路径前加“.”或者 source 的方式,`可以使脚本内容在当前 shell 里执行,而无需打开子 shell!`这也是为什么我们每次要修改完
575 1
Shell脚本的常用执行方式、bash 和 sh 的关系、子shell、Centos 默认的解析器是 bash、Linux 提供的 Shell 解析器、Shell 概述、Shell 脚本入门
|
11月前
|
JavaScript 前端开发 Shell
别再用 bash 写前端自动化脚本了!
Vladimir[1] 发现自己一直讨厌 bash 编写的自动化流程脚本,并且在机缘巧合下发现同事们都有类似的想法,因此他分享了他认为 JavaScript 编写自动化脚本的优势,看看能不能说服大家去共建更好的生态。
|
11月前
|
Unix Shell Linux
关于 Bash 脚本中 Shebang 的趣事
关于 Bash 脚本中 Shebang 的趣事
|
移动开发 Unix Shell
shell脚本 解决“/bin/bash^M: bad interpreter: No such file or directory”
shell脚本 解决“/bin/bash^M: bad interpreter: No such file or directory”
94 0
|
Ubuntu Shell Linux
Shell脚本的常用执行方式、bash 和 sh 的关系、子shell、Centos 默认的解析器是 bash、Linux 提供的 Shell 解析器、Shell 概述、Shell 脚本入门
Shell脚本的常用执行方式、bash 和 sh 的关系、子shell、Centos 默认的解析器是 bash、Linux 提供的 Shell 解析器、Shell 概述、Shell 脚本入门
Shell脚本的常用执行方式、bash 和 sh 的关系、子shell、Centos 默认的解析器是 bash、Linux 提供的 Shell 解析器、Shell 概述、Shell 脚本入门