Why is it better to use "#!/usr/bin/env NAME" instead of "#!/path/to/NAME" as my shebang?

  • I notice that some scripts which I have acquired from others have the shebang #!/path/to/NAME while others (using the same tool, NAME) have the shebang #!/usr/bin/env NAME.

    Both seem to work properly. In tutorials (on Python, for example), there seems to be a suggestion that the latter shebang is better. But, I don't quite understand why this is so.

    I realize that, in order to use the latter shebang, NAME must be in the PATH whereas the first shebang does not have this restriction.

    Also, it appears (to me) that the first would be the better shebang, since it specifies precisely where NAME is located. So, in this case, if there are multiple versions of NAME (e.g., /usr/bin/NAME, /usr/local/bin/NAME), the first case specifies which to use.

    My question is why is the first shebang preferred to the second one?

    @TheGeeko61: In my case I had something broken and some variables wasn't in env. So I suggest to use this shebang to verify if env is correctly loaded.

  • F1Linux

    F1Linux Correct answer

    one year ago

    Objective Criteria/Requirements:

    In determining whether to use an absolute or logical (/usr/bin/env) path to an interpreter in a she-bang, there are (2) key considerations:

    a) The interpreter can be found on target system

    b) The correct version of interpreter can be found on target system

    If we AGREE that "b)" is desirable, we also agree that:

    c) It's preferable our scripts fail rather than execute using an incorrect interpreter version and potentially achieve inconsistent results.

    If we DON'T AGREE that "b)" matters, then any interpreter found will suffice.

    Testing:

    Since using a logical path- /usr/bin/env to the interpreter in the she-bang is the most extensible solution allowing the same script to execute successfully on target hosts with different paths to the same interpreter, we'll test it- using Python due to its' popularity- to see if it meets our criteria.

    1. Does /usr/bin/env live in a predictable, consistent location on POPULAR (not "every") Operating Systems? Yes:

      • RHEL 7.5
      • Ubuntu 18.04
      • Raspbian 10 ("Buster")
      • OSX 10.15.02
    2. Below Python script executed both inside and outside of virtual envelopes (Pipenv used) during tests:

      #!/usr/bin/env pythonX.x import sys print(sys.version) print('Hello, world!')

    3. The she-bang in the script was toggled by Python version number desired (all installed on same host):

      • #!/usr/bin/env python2
      • #!/usr/bin/env python2.7
      • #!/usr/bin/env python3
      • #!/usr/bin/env python3.5
      • #!/usr/bin/env python3.6
      • #!/usr/bin/env python3.7
    4. Expected results: that print(sys.version) = env pythonX.x. Each time ./test1.py was executed using a different installed Python version, the correct version specified in the she-bang was printed.

    5. Testing Notes:

      • Tests were exclusively limited to Python
      • Perl: Like Python- MUST live in /usr/bin according to the FHS
      • I've not tried every possible combination on every possible number of Linuxy/Unixy Operating System and version of each Operating System.

    Conclusion:

    Although it's TRUE that #!/usr/bin/env python will use the first version of Python it finds in the user's Path, we can moderate this behaviour by specifying a version number such as #!/usr/bin/env pythonX.x. Indeed, developers don't care which interpreter is found "first", all they care about is that their code is executed using the specified interpreter they know to be compatible with their code to ensure consistent results- wherever that may live in the filesystem...

    In terms of portability/flexibility, using a logical- /usr/bin/env - rather than absolute path not only meets requirements a), b) & c) from my testing with different versions of Python, but also has the benefit of fuzzy-logic finding the same version interpreter even if they live at different paths on different Operating Systems. And although MOST distros respect the FHS, not all do.

    So where a script will FAIL if binary lives in different absolute path then specified in shebang, the same script using a logical path SUCCEEDS as it keeps going until it finds a match, thereby offering greater reliability & extensibility across platforms.

    Excellent analysis. We appreciate it.

    The question wasn't specific to Python. I don't think you can safely assume that other interpreters will be installed as, for example `interpX` *and* `interpX.x`. For example, the system I'm using at the moment has `perl`, `perl5.26.3`, and `perl5.30.1`. Another has `perl` and `perl5.26.1`. Neither has a `perl5` command. Also, you didn't mention where the `pythonX` and `pythonX.x` interpreters on any of the systems you tested were installed. If they were all in `/usr/bin` then your experiment doesn't demonstrate any advantage of `#!/usr/bin/env pythonX.x` over `#!/usr/bin/pythonX.x`.

    @KeithThompson Chose Python for example due to it's huge popularity. The other answers were general and I wanted to take a more practical approach to answering the question. As specifically relates to Perl- I note in my updated answer (responding to your valid concerns) in "Testing">"Notes"- this too must live in `/usr/bin` according to the FHS. Although I though it was implied in my answer, added a pp in Conclusions that where both options would yield same result the ***logical*** path has added benefit of offering greater reliability where binaries might live in diff paths on diff systems

    Note that not every Linux system complies to the FHS, not by a long shot. And, paraphrasing the old saying, "Not all the world is Linux". Many Unix(y) systems have Python, Perl, even bash or tcsh as "optional, unsupported third party additions", presumably under `/usr/local` or `/opt/*packagename*` or some really exotic placements.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM