没有#!的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环境下使用Bash脚本实现日志监控
在Linux环境下,为防止员工泄密,本文提出使用Bash脚本进行日志监控。脚本会定期检查系统日志文件,搜索敏感关键词(如&quot;password&quot;、&quot;confidential&quot;、&quot;secret&quot;),并将匹配项记录到临时日志文件。当检测到可疑活动时,脚本通过curl自动将数据POST到公司内部网站进行分析处理,增强信息安全防护。
176 0
|
29天前
|
Devops 关系型数据库 大数据
1000个开源免费的bash脚本合集
【10月更文挑战第4天】
|
6月前
|
存储 Shell Linux
Linux Bash 脚本中的 IFS 是什么?
【4月更文挑战第25天】
127 0
Linux Bash 脚本中的 IFS 是什么?
|
3月前
|
Shell
一个能够生成 Markdown 表格的 Bash 脚本
【8月更文挑战第20天】这是一个使用Bash脚本生成Markdown表格的示例。脚本首先设置表头与内容数据,然后输出Markdown格式的表格。用户可以根据需要自定义表格内容。使用时,只需将脚本保存为文件(如 `generate_table.sh`),赋予执行权限,并运行它,即可在终端看到生成的Markdown表格。
|
3月前
|
Unix Shell Linux
在Linux中,什么是Bash脚本,并且如何使用它。
在Linux中,什么是Bash脚本,并且如何使用它。
|
3月前
|
Shell 开发者
深入理解Bash脚本中的函数
【8月更文挑战第20天】
49 0
|
3月前
|
存储 Shell 数据处理
深入探讨Bash脚本中的数组
【8月更文挑战第20天】
30 0
|
3月前
|
存储 Shell
Bash 脚本中的 `hash` 命令
【8月更文挑战第19天】
30 0
|
5月前
|
Unix Shell Linux
技术经验分享:Bash脚本命令使用详解
技术经验分享:Bash脚本命令使用详解
45 0
|
6月前
|
存储 弹性计算 运维
用bash脚本创建目录
【4月更文挑战第29天】
51 3