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

前端 未结 4 837
野的像风
野的像风 2021-02-19 07:19

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

相关标签:
4条回答
  • 2021-02-19 08:00

    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.

    0 讨论(0)
  • 2021-02-19 08:12

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

    0 讨论(0)
  • 2021-02-19 08:17

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

    0 讨论(0)
  • 2021-02-19 08:18

    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 /bin/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).

    Thus the trick of using env to write portable scripts — env just happens to do a PATH lookup. (It's also useful in other places that don't do PATH lookups, e.g. docker exec.)

    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 were introduced, and tooling grew to copy the parts you need into it.

    /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.

    0 讨论(0)
提交回复
热议问题