写脚本的时候通常会在脚本的开头加上 shebang, 系统会将这段内容作为解释器指令,比如 bash shell 脚本。

$> cat example
#!/usr/bin/bash
echo "HaHa"

$> chmod +x ./example

$> ./example
HaHa

只要为脚本添加了可执行的属性,那么内核在执行脚本的时候,会调用 shebang 描述的解释器来执行脚本。 ./exmaple 其实等价于 /usr/bin/bash ./example。shebang 描述的解释器需要写其绝对路径或者相对路径,因为内核并不会在用户设置的 PATH 里找解释器。关于 shebang,讨论最多的应该是 兼容性版本控制 问题。

兼容性

Linux 和 Unix 在存放解释器的具体路径不太一致,比如 Linux 会放到 /usr/bin/ 中,而 openBSD 会放到 /usr/local/bin/ 中。不同包管理器在安装解释器的时候,存放的位置也不尽相同。 当你在 Mac 上写了 shell 脚本,测试并提交到代码库。 结果等到部署的那一天,执行脚本的时候发现找不到解释器了。 为了解决这个问题,可以通过 env 来解决,因为它在 Linux 和 Unix 存放的位置相同。

$> cat exmaple
#!/usr/bin/env bash
echo "HaHa"

env 会在用户设置的 PATH 中查找解释器第一次出现的具体路径。 虽然办法比较 tricky,但是这种方式能解决脚本解释器的兼容性问题。

版本控制

env 会在用户配置的 PATH 中查找解释器第一次出现的具体路径。 这个机制就说明这存在两个问题:

  • 不同用户配置的 PATH 内容不同,导致找到的解释器版本会出现不一致
  • 很难通过 env 的方式来做到版本控制

所以有些人坚持不用 /usr/bin/env cmd 这种方式。

思考

从部署的角度看,线上机器的环境都是一致的,而且都是通过自动化脚本去安装各种依赖。 版本控制较细,这种情况下,不太建议采用 env 这种方式。如果从开发者的角度看, 还是希望脚本能做到兼容,毕竟开发者的环境千差万别,env 基本上能解决这一大痛点。

不用 env 这种方式,就得确保测试环境和线上机器是一致的,通常 Docker 和 虚拟机都能解决这样的问题。