背景
在使用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
的环境变量中。