在 shell(Bash 是一种 shell) 中执行外部程序和脚本时,Linux 内核会启动一个新的进程,以便在新的进程中执行指定的程序或脚本。内核知道该如何为编译型的程序做这件事,但是对于脚本程序呢?当 shell 要求内核执行一个脚本文件时,内核是不知道该怎么办的!所以它回应一个 "not executable format file" 的错误消息。Shell 收到这样的消息后会做出类似下面的判断:这不是个编译型程序,那它肯定是一个 shell 脚本;接着就启动一个新的 /bin/sh 副本来这些该程序。

当系统中只有一个 shell(/bin/sh) 时这并没有什么问题。但是当前的系统中一般都存在多个 shell,比如 Bash、Dash等等。因此需要通过一种方式,告诉 Linux 内核应该以哪个 shell 来执行指定的脚本。实时上,这么做有助于执行机制的通用化,让用户可以直接引用任何的程序语言解释器,而不仅仅是一个 shell。具体的方法是通过脚本文件中特殊的第一行来设置:在第一行的开头处使用 #! 这两个字符(英文一般称为 shebang)。

当一个脚本中第一行是以 #! 这两个字符开头时,内核会扫描该行的其余部分,看是否可以找到可以用来执行该脚本文件的解释器。所以这是一种非常通用的做法,因为除了 shell 我们还可以指定其它的解释器,比如:

#!/usr/bin/awk
# 这个脚本是一个 awk 程序

#!/bin/bash

直接指定 shell 的绝对路径是一种经典的写法。这样内核会直接调用你指定的解释器,并把脚本文件作为参数传递给它。这样做的缺点也非常明显,面对多如牛毛的 Linux 发行版,你无法保证所有系统中的 bash 程序都放置在 /bin 目录下。当然其它程序的路径就更无法保证了。

/usr/bin/env  命令

让我们先来了解一下 /usr/bin/env 命令的执行方式,比如下面的命令:

$ env name=value name2=value2 program args

这会使用环境变量和由 name=value 和 name2=value2 指定的值扩展当前环境而形成的环境运行命令 program args。如果不包含任何参数,比如 name=value,那么将传递不经过修改的当前环境。因为 env 是外部命令,所以它并不知道 bash 中的别名,env 只是将程序和参数传递给 exec 调用。

#!/usr/bin/env bash

在了解了 /usr/bin/env 命令之后,让我们来看看 shebang 的另一种写法:

#!/usr/bin/env bash

你会看到越来越多的脚本采用了这种写法。通过 /usr/bin/env 运行命令的好处是可以在当前环境中查找程序的默认版本。这样,就不必在系统上的特定位置查找它,因为这些路径在不同的系统中可能位于不同的位置。只要你指定的解释器程序在你的 PATH 变量中,这种写法就会找到它。当然,这么做的前提是 /usr/bin/env 必须存在。
这种写法也是有缺点的,比如我们可以创建一个名称为 bash 的程序,并把它的路径添加到 PATH 变量的靠前位置,这样就会使用你写的假 bash 程序来执行脚本,而不是真正的 bash 程序,这是一个安全隐患。

个人的理解

#!/usr/bin/env bash 写法
更灵活,可移植性较好,但是有安全风险。

#!/bin/bash 写法
如果只考虑在单一的系统中执行,足够了。

参考:
Shebang (Unix)

Bash Shebang 小结的更多相关文章

  1. bash操作小结

    刚开始学写bash脚本,发现有很多需要注意的细节问题,在这里记录一下便于记忆: 1. help test  帮助 2. bash提供的数组数据结构,它是以数字为下标的,和C语言从0开始的下一样  参考 ...

  2. bash学习记录

    bash: 管理员:  提示符# 普通用户:提示符$ 环境变量 A=3(变量是指内存空间,A指的是内存空间的名称-变量标示符) PS1  \u@\h:\w\$  \u用户名 \h主机名 \w工作目录的 ...

  3. WebStorm换主题(护眼)

    一.下载喜欢颜色的主题 http://www.phpstorm-themes.com/ 我用的豆沙绿护眼 <scheme name="Solarized Light My" ...

  4. linux shell攻略学习笔记一 基础篇

    1.#!/bin/bash shebang 可以自定义 比如 #!/bin/bash +x 就会打印出执行日志 linux中 \ 代表null \n2\n3” 会转义其中的\n,生成3行数据 $! 保 ...

  5. Linux 下如何隐藏自己不被发现?

    可能在某些情况下,自己运行的程序不想或者不方便被其他人看到,就需要隐藏运行的进程.或者某些攻击者采用了本文介绍的隐藏技术,也可以让大家看到如何进行对抗. 隐藏有两种方法: kernel 层面,不对用户 ...

  6. Linux Shell 学习笔记 00

    1.Bash = Bourne Again SHell 2.终端提示符: #普通用户 username@hostname$ #管理员用户 root@hostname# 3.shell脚本通常是一个以s ...

  7. BASH 命令以及使用方法小结【转】

    1,export VAR=... 这个命令在Shell下直接运行可以使之后运行的脚本也知道这个VAR.但是如果 这个命令在脚本中运行,那么不影响脚本以外的参数.举个例子,如果在一个脚本运行之前没有 V ...

  8. linux bash & profile &bash_profile 小结

    login 方式:: su - oracle 依次 /etc/bash.bashrc———— /home/$user/.bashrc ———— /ect/profile ———— /home/$use ...

  9. BASH 命令以及使用方法小结

    最近工作中需要写一个Linux脚本,用到了很多BASH命令,为了防止以后忘记,在这里把它们一一记下来.可能会比较乱,随便看看就好了.如果有说的不对的地方也欢迎大家指正. 1,export VAR=.. ...

随机推荐

  1. web service && WCF 学习小结

    Web Service和WCF技术都提供了应用程序与应用程序之间的通信.它们都是基于soap消息在客户端和服务端之间进行通信,由于soap消息是一种xml格式,因此传输的数据格式为XML.每次客户端向 ...

  2. Windows:Oracle 11g 备份脚本

    @echo off echo ================================================ echo Windows环境下Oracle数据库的自动备份脚本 echo ...

  3. 4.7 Sublime Text3 中配置 Python环境 --之上安装Sublime 3

    返回总目录 目录: 1.展示效果: 2.缺优分析: 3.下载Sublime Text3 (一)展示效果: 1.能够交互式编写Python代码: 2.可以编写文件式Python代码: 3.能够自动补齐代 ...

  4. mysql覆盖索引详解

    覆盖索引的定义: 如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’.即只需扫描索引而无须回表. 只扫描索引而无需回表的优点:    1.索引条目通常远小于数据行大小,只需要读取索引, ...

  5. iOS网络篇

    iOS网络请求三步: 1.新建URL连接 2.新建请求(请求新建的URL连接) 3.建立连接. 然后就可以获取数据了. 一.同步GET请求方法 -(void)synchronizationGet { ...

  6. golang的reflection(转)

    作者:BGbiao 链接:https://www.jianshu.com/p/42c19f88df6c 來源:简书 反射reflection 可以大大提高程序的灵活性,使得interface{}有更大 ...

  7. svn 更新

    dev更新流程: 1.打开软件,文件—>打开,弹出右边对话框链接dev地址      2.在窗口输入cd /var/www/user 回车 3.输入svn up 则更新dev代码完成 本地提交到 ...

  8. js中采用词法作用域

    所谓的 词法( 代码 )作用域, 就是代码在编写过程中体现出来的作用范围. 代码一旦写好, 不用执行, 作用范围就已经确定好了. 这个就是所谓词法作用域. 在 js 中词法作用域规则: 1.函数允许访 ...

  9. leetcode 338. Counting Bits,剑指offer二进制中1的个数

    leetcode是求当前所有数的二进制中1的个数,剑指offer上是求某一个数二进制中1的个数 https://www.cnblogs.com/grandyang/p/5294255.html 第三种 ...

  10. python 获取当前路径

    使用os模块: os.path.realpath(__file__)