What's the difference of using #!/usr/bin/env or #!/bin/env in shebang?
Asked Answered
A

4

18

Will there be any difference or it's just a personal choice?

Andry answered 5/4, 2011 at 8:15 Comment(0)
B
24

#!<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.

Bobo answered 5/4, 2011 at 8:20 Comment(2)
Adding to that, non-OSX BSDs don't have /bin/bash so /usr/bin/env is recommended for portability there. Also, if you want to run a newer version of BASH in a different directory lined up in $PATH, env would respect and use that, whereas /bin/bash obviously hardcodes that. – Agonistic
Specially, OS/X has /usr/bin/env and does not have a symbolic link from /bin/env to /usr/bin/env. You would get a -bash: ./your_commnd: /bin/env: bad interpreter: No such file or directory error. – Evante
S
10

Historically, UNIX had 2 bunches of binaries:

  • / 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:

  • in /usr/bin/ but NOT /bin
  • in /bin but NOT /usr/bin
  • in both, same as symlink
  • in both but different! E.g. some systems played with /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.

What changed

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 (initial ramdisk) were introduced, and tooling grew to copy the parts you need into the initrd.

/usr unification

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.

/usr/local/, ~/.local/ and other places

There are more reasons to want PATH lookup:

  • Not all unixes install all languages out of the box, or what they package is too old. So while stuff like 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...
  • Many language version / library managers have a way to run with an isolated environment, and frequently such environments are activated via executable "shims". For example, a Python "virtualenv" directory has a bin/python3; so when you prepend that bin/ directory to your PATH, and if you run scripts that do #!/usr/bin/env python3, that's all you need to use modules specific to that enviroment.

Back to the question, what full path to use for 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) 🀷

https://www.in-ulm.de/~mascheck/various/shebang/ site has extensive info on all aspects of #! portability, and says /usr/bin/env works almost everywhere.

Shit answered 16/4, 2019 at 11:6 Comment(0)
B
2

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.

Bates answered 5/12, 2012 at 11:15 Comment(1)
Handling of multiple args varies by unix. See table in-ulm.de/%7Emascheck/various/shebang/#results β€” what you describe is "all args in one", IIUC represented as blank in that table column, which is indeed majority, but there are exceptions e.g. recent OSX. But the conclusion is same: don't add flags after #!/usr/bin/env python, highly non-portable. – Shit
H
0

/usr/bin/env is a soft link to /bin/env. Essentially, you are using /bin/env

Highup answered 5/4, 2011 at 8:29 Comment(6)
which system has both as hard files? – Highup
Ubuntu does not have /bin/env. Solaris 8 has both files separately. – Cot
@Highup - most systems do not have /bin/env at all. /usr/bin/env in the standard location. – Tradespeople
Centos 4 has /bin/env and /usr/bin/env is a symlink – Foothill
Fedora completed fedoraproject.org/wiki/Features/UsrMove, so nowdays /bin is symlink -> /usr/bin; RHEL/CentOS followed suit. Many other distros haven't, or are still debating it. See wiki.debian.org/UsrMerge, lwn.net/Articles/483921. – Shit
in-ulm.de/~mascheck/various/shebang/#env also mentions reverse situation: "On the other hand, there's only /bin/env at least on SCO OpenServer 5.0.6 and Cray Unicos 9.0.2 (although the latter is only of historical interest). " That page has extensive research on all aspects of #! portability. – Shit

© 2022 - 2024 β€” McMap. All rights reserved.