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 on many systems, 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.
UPDATE: the env
of FreeBSD has a -S
option (copied by GNU coreutils env
in 8.30 with a --split-string
alias) that allows passing multiple arguments, so for example #!/usr/bin/env csh -f
will fail on systems whose shebang accepts only one argument after the interpreter, but #!/usr/bin/env -S csh -f
will work on those such as Linux where what's after the interpreter is bundled into one argument. Many env
implementations such as NetBSD
's, busybox
, toybox
still don't support it so that won't help you write portable scripts.
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).
Kevin's comment points out that Python's virtualenv
creates a special case, where the environment installs a Python interpreter in a special directory that's inserted at the front of $PATH
. For that particular environment (and perhaps others like it), the #!/usr/bin/env python
trick (or python3
?) is likely to be the best solution. (I haven't used virtualenv myself.)