背景

在使用shell脚本或者Python脚本的时候,有的人喜欢使用#!/usr/bin/env NAME指定解析程序,而有的人喜欢使用#!/usr/bin/NAME,前者往往被认为兼容性更好,所以应该优先使用,本文将仔细分析下这种写法的利弊。

分析

首先,可以仔细看下env命令的手册,这里我们只节选开头部分

NAME
       env - run a program in a modified environment

SYNOPSIS
       env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]

可以看到,使用env这种方式指定你的解析程序的时候,系统会在PATH环境变量中查找对应的解析程序,当系统中有不同的解析程序版本的时候可以通过设置PATH环境变量的方式去指定优先使用某个版本,还有,如果你的脚本会被移植到不通的系统中去,可移植性也会更好。
但是,任何事物都有两面性,使用#!/usr/bin/env NAME去指定解析程序也不例外。
设想一种情况,你在系统中安装了一个名字叫program-a的解析程序,放在了/directory-a目录下,然后将系统环境变量PATH修改为:
PATH=/directory-a:$PATH
然后你在脚本中使用#!/usr/bin/env program-a指定解析程序,后面另一位同事在不知情的情况下,在这台服务器上也安装了一个叫做program-a的解析程序(版本不同,或者更甚功能也有很大差异),放在了/directory-b下,并且将PATH修改为:
PATH=/directory-b:$PATH
这样的话如果你的脚本就有可能因为使用了错误的解析程序而失效。
还有一种常见的情况,在Linux中使用crontab命令添加周期性任务的时候,如果这个任务是自己写的一个脚本,而且在脚本中使用了#!/usr/bin/env NAME指定解析程序的话,也存在出错的风险,因为crontab默认只使用很有限的环境变量,可以使用cat /etc/crontab查看当前系统中crontab的默认配置

$cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

这样的话,如果你使用env指定你的解析程序,而这个解析程序又恰好不在crontab配置的PATH环境变量路径中的话就有可能导致你的任务出错。

总结

使用#!/usr/bin/env NAME的方式指定解析程序并不一定是好的,你得先考虑清楚自己的脚本在什么样的环境中运行,会不会被移植到其它系统,或者你的脚本会被crontab执行的话,就要注意自己的解析程序是否在crontab的环境变量中。


峰哥的白板报
13 声望0 粉丝