Will there be any difference or it's just a personal choice?
#!<interpreter> <arguments>
tries to run <interpreter> <arguments>
to read and run the rest of the file.
So #!/usr/bin/env
means that there must be a program called /usr/bin/env
;
#!/bin/env
means that there must be a program called /bin/env
.
Some systems have one and not the other.
In my experience, most have /usr/bin/env
, so #!/usr/bin/env
is more common.
Unix systems will try to run <interpreter>
using execve
, which is why it must be a full path, and #!env
without a path will not work.
/
filesystem mounted early on boot./usr
might have been mounted later, possibly running scripts and programs from /
to arrange the mount. Example: some sites saved space by mounting /usr from network, but you need to get on the network first. Example: it's a large local filesystem, but if it gets damaged, you want tools like fsck
to try fixing it.Thus, /bin
and /sbin
(using /lib
), had to contain a minimal system including at least a shell at /bin/sh, scripting essentials like /bin/echo
, /bin/test
etc., system tools like /bin/mount
or /sbin/mount
, and /sbin/fsck
...
Thus in different unixes, almost any program might have been:
/bin/sh
being a very minimal shell (e.g. dash
) for faster startup, but symlinking /usr/bin/sh
-> /usr/bin/bash
(iirc, invoking bash as "sh" puts it into some posix mode, but it's still a different more powerful shell).Most of the time, one just sets $PATH to include both areas. But some contexts like #!
or docker exec
require a fixed full path. Thus the trick of using env
to write portable scripts — env
just happens to do a PATH lookup.
These were valid use cases but modern Linux followed a similar argument of "need small userspace to mount / recover main userspace" for /
itself too! The pivot syscall and initrd
were introduced, and tooling grew to copy the parts you need into it.
Now, /
vs /usr
arguably lost its purpose. Having both on one filesystem and symlinking was doable for everybody in principle, though some particular setups would break and would have to change...
See https://lwn.net/Articles/483921/ from 2012 for overview of this "/usr unification" idea. For example Fedora completed it: https://fedoraproject.org/wiki/Features/UsrMove. Many other distros haven't, or are still debating it, or ironing out some kinds to break less users. For example see debian's preparation: https://wiki.debian.org/UsrMerge.
There are more reasons to want PATH lookup:
env
always exists in /bin or /usr/bin or both, one might only have (or prefer) python and other interpreters in /usr/local, or under your home dir...#!/usr/bin/env python3
, that's all you need to use modules specific to that enviroment.env
itself?By same logic, in systems with a distinct /usr, env
itself might be missing from either place so you can't write a 100.00% portable #!
line.
In practice both are likely to work. I don't have stats, but in practice for many years I've seen /usr/bin/env
as the more commonly recommended form (example) 🤷
Mikel explanation is great, it misses just a small fact (which is rather important), it's only one argument being passed including all spaces:
#!<Interpreter> <argument>
Results in calling:
$ <Interpreter> '<argument>' path_to_calling_script
So for Example:
$ cat /tmp/test
#!/usr/bin/env python
print "hi"
$ /tmp/test
is the same as calling:
$ /usr/bin/env "python" /tmp/test
The quotes try to show that if you add any flag or other values will be part of the argument being called.
#!/bin/bash -c /bin/env python
Will be interpreted as:
$ /bin/bash "-c /bin/env python"
Which won't work.
/usr/bin/env
is a soft link to /bin/env
. Essentially, you are using /bin/env
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With