1

In the pip program, the She-bang is

#!/usr/local/bin/python

if __name__ == "__main__":
    # Python program body

while in the Install Certificates.command that Python Launcher offers:

#!/bin/sh

/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6 << "EOF"

# python program body

EOF

Are there any differences between those two approaches? And is there any reason to prefer one to another?

It seems to me they are all the same, except for the second one has one more bash subroutine. Is this right?

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97
Dogemore
  • 590
  • 1
  • 5
  • 18
  • 1
    As an aside, `#!/user/bash` is probably not useful on any mainstream platform. It could be the home directory of a user whose account name is `bash` on a MacOS system. I'm guessing you are probably actually looking at `#!/usr/bin/bash` (and the other typos reinforce this impression). – tripleee Feb 03 '19 at 09:46
  • There is no bash "subroutine" here. The second script is just a shell script which runs Python. – tripleee Feb 03 '19 at 09:53
  • @tripleee Yes, that is a typo, and I've corrected it. Thanks. – Dogemore Feb 03 '19 at 11:48

3 Answers3

3

In the general case, you simply want to specify the interpreter you actually want.

Outside of this, you sometimes see workarounds like this as portability hacks. On POSIX systems, /usr/bin/env covers the majority of scenarios quite nicely; but if you need portability to older or otherwise peculiar systems, falling back to the lowest common denominator and then working your way back up to a place where you can reliably run e.g. Python on a variety of systems may require all kinds of unobvious constructs. (The previous - upvoted! - answer by Dan D. is a good example.)

There are also cases where you want sh to set something up (fetch some environment variables which are specified in a file which uses sh syntax, for example) and then hand over execution to Python;

#!/bin/sh
# source some variables
. /etc/defaults/myenv.sh
# Then run Python
exec env python -c '
# ... Your Python script here
    ' "$@"
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • 1
    Another common example is [the Perl "preamble".](/questions/2308874/explain-the-deviousness-of-the-perl-preamble) – tripleee Feb 03 '19 at 09:39
2

There is a line length limit on the #! line. Perhaps they did that to get around that.

The options are the path to the program but only if it is short enough. Use of env python which uses the path. Or chain loading like this.

Dan D.
  • 73,243
  • 15
  • 104
  • 123
  • Are you sure? I just tested it and found the line limit on shebang on Mac OS X is 511 characters. [Usually](https://stackoverflow.com/a/10813634/3787051) it is 127 characters. But they were nowhere near either of those limits. – Alex Harvey Feb 03 '19 at 07:50
  • 1
    @AlexHarvey I can't be. We don't know how long the ellipsis in the path is. But perhaps the author of that file thought the limit was much lower and didn't bother testing before using the workaround. – Dan D. Feb 03 '19 at 08:11
  • Or the real path to Python could be even longer on some systems they need to support. A "one size fits all" solution is going to be easier to debug, maintain, and document. – tripleee Feb 03 '19 at 09:51
  • There are multiple versions of pip on my MBP, and the longer one I can find is `#!/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6` Which is already quite long. But it's only 65 characters. Even if the line limit is 127, it's still quite hard to hit double the length of this path. And this is set by default when installing pip with python 3.6. So I don't know if this would be the reason for this. – Dogemore Feb 03 '19 at 11:41
  • @tripleee, fyi the source code refs are [here](https://github.com/python/cpython/blob/f75d59e1a896115bd52f543a417c665d6edc331f/Mac/BuildScript/build-installer.py#L1625-L1626) and [here](https://github.com/python/cpython/blob/f75d59e1a896115bd52f543a417c665d6edc331f/Mac/BuildScript/resources/install_certificates.command#L3). This code only seems to exist for making Mac OS X DMG packages. I guess as suggested, the author just didn't know what the shebang line limit was? Puzzling code! – Alex Harvey Feb 03 '19 at 12:36
  • I believe I have explained why the author wrote the code this way in my answer, after a bit of digging. – Alex Harvey Feb 03 '19 at 13:12
2

This specific code for the Install Certificates.command script was introduced in Python Issue #17128. As far as I can tell, the author hasn't explained why he wrote the code this way.

Note that .command files are Shell scripts on Mac OS X that can be executed by double-clicking on them in Finder.

I believe the likely explanation is that the author simply wanted to honour Mac OS X's expectation that .command files should be Shell scripts.

You could test this by placing the following content in a file ~/Desktop/test.command:

#!/usr/bin/env python
print "Hello world"

Then view the Desktop folder in Finder, and note that it is reported as a "shell" file:

enter image description here

(Although it is reported incorrectly as a Shell file, this Python script can still be executed by double-clicking on it. It doesn't break Finder or anything.)

To answer the specific question, one reason for preferring this pattern might be, as Dan D. said, to avoid a Shebang line limit.

In general, you would prefer to use #!/usr/bin/env python as your Shebang line. Creating a Bash Heredoc (i.e. the python3.6 << EOF pattern) would create all sorts of problems, such as your syntax highlighting won't work, you have to watch out for Bash variable interpolation inside the Heredoc, etc.

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97