Why is it better to use "#!/usr/bin/env NAME" instead of "#!/path/to/NAME" as my shebang?
It isn't necessarily better.
The advantage of #!/usr/bin/env python
is that it will use whatever python
executable appears first in the user's $PATH
.
The disadvantage of #!/usr/bin/env python
is that it will use whatever python
executable appears first in the user's $PATH
.
That means that the script could behave differently depending on who runs it. For one user, it might use the /usr/bin/python
that was installed with the OS. For another, it might use an experimental /home/phred/bin/python
that doesn't quite work correctly.
And if python
is only installed in /usr/local/bin
, a user who doesn't have /usr/local/bin
in $PATH
won't even be able to run the script. (That's probably not too likely on modern systems, but it could easily happen for a more obscure interpreter.)
By specifying #!/usr/bin/python
you specify exactly which interpreter will be used to run the script on a particular system.
Another potential problem is that the #!/usr/bin/env
trick doesn't let you pass arguments to the intrepreter (other than the name of the script, which is passed implicitly). This usually isn't an issue, but it can be. Many Perl scripts are written with #!/usr/bin/perl -w
, but use warnings;
is the recommended replacement these days. Csh scripts should use #!/bin/csh -f
-- but csh scripts are not recommended in the first place. But there could be other examples.
I have a number of Perl scripts in a personal source control system that I install when I set up an account on a new system. I use an installer script that modifies the #!
line of each script as it installs it in my $HOME/bin
. (I haven't had to use anything other than #!/usr/bin/perl
lately; it goes back to times when Perl often wasn't installed by default.)
A minor point: the #!/usr/bin/env
trick is arguably an abuse of the env
command, which was originally intended (as the name implies) to invoke a command with an altered environment. Furthermore, some older systems (including SunOS 4, if I recall correctly) didn't have the env
command in /usr/bin
. Neither of these is likely to be a significant concern. env
does work this way, a lot of scripts do use the #!/usr/bin/env
trick, and OS providers aren't likely to do anything to break it. It might be an issue if you want your script to run on a really old system, but then you're likely to need to modify it anyway.
Another possible issue, (thanks to Sopalajo de Arrierez for pointing it out in comments) is that cron jobs run with a restricted environment. In particular, $PATH
is typically something like /usr/bin:/bin
. So if the directory containing the interpreter doesn't happen to be in one of those directories, even if it's in your default $PATH
in a user shell, then the /usr/bin/env
trick isn't going to work. You can specify the exact path, or you can add a line to your crontab to set $PATH
(man 5 crontab
for details).
Because /usr/bin/env can interpret your $PATH
, which makes scripts more portable.
#!/usr/local/bin/python
Will only run your script if python is installed in /usr/local/bin.
#!/usr/bin/env python
Will interpret your $PATH
, and find python in any directory in your $PATH
.
So your script is more portable, and will work without modification on systems where python is installed as /usr/bin/python
, or /usr/local/bin/python
, or even custom directories (that have been added to $PATH
), like /opt/local/bin/python
.
Portability is the only reason using env
is preferred to hard coded paths.
Specifying the absolute path is more precise on a given system. The downside is that it's too precise. Suppose you realize that the system installation of Perl is too old for your scripts and you want to use your own instead: then you have to edit the scripts and change #!/usr/bin/perl
to #!/home/myname/bin/perl
. Worse, if you have Perl in /usr/bin
on some machines, /usr/local/bin
on others, and /home/myname/bin/perl
on yet other machines, then you'd have to maintain three separate copies of the scripts and execute the appropriate one on each machine.
#!/usr/bin/env
breaks if PATH
is bad, but so does almost anything. Attempting to operate with a bad PATH
is very rarely useful, and indicates that you know very little about the system the script is running on, so you can't rely on any absolute path anyway.
There are two programs whose location you can rely on on almost every unix variant: /bin/sh
and /usr/bin/env
. Some obscure and mostly retired Unix variants had /bin/env
without having /usr/bin/env
, but you're unlikely to encounter them. Modern systems have /usr/bin/env
precisely because of its widespread use in shebangs. /usr/bin/env
is something you can count on.
Apart from /bin/sh
, the only time you should use an absolute path in a shebang is when your script isn't meant to be portable, so you can count on a known location for the interpreter. For example, a bash script that only works on Linux can safely use #!/bin/bash
. A script that is only meant to be used in-house can rely on house interpreter location conventions.
#!/usr/bin/env
does have downsides. It's more flexible than specifying an absolute path but still requires knowing the interpreter name. Occasionally you might want to run an interpreter that isn't in the $PATH
, for example in a location relative to the script. In such cases, you can often make a polyglot script that can be interpreted both by the standard shell and by your desired interpreter. For example, to make a Python 2 script portable both to systems where python
is Python 3 and python2
is Python 2, and to systems where python
is Python 2 and python2
doesn't exist:
#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
exec python2 "$0" "$@"
else
exec python "$0" "$@"
fi
'''
# real Python script starts here
def …