Search code examples
pythonpython-3.xshellnixshebang

MacOS: Shebang with absolute path not working


Selecting a Python interpreter using a shebang doesn't seem to work in some cases.

I'm trying to use my python3.9 interpreter in a shebang (#!/...) on MacOS. See the Python-file below called hello.py:

#!/nix/store/i46k148mi830riq4wxh49ki8qmq0731k-python3-3.9.2-env/bin/python3.9
print("Hello world")

Now, I'll make sure this file is executable, and try to execute it:

$ chmod +x ./hello.py
$ ./hello.py

./hello.py: line 2: syntax error near unexpected token `"Hello world"'
./hello.py: line 2: `print("Hello world")'

That's weird. Did I mistype something? Let me try to execute it with my interpreter directly to see if it works:

$ /nix/store/i46k148mi830riq4wxh49ki8qmq0731k-python3-3.9.2-env/bin/python3.9 ./hello.py

Hello world

That seems to work fine. Why did the shebang fail? Let me try with a different interpreter:

#!/usr/bin/python3
print("Hello world")
$ ./hello.py

Hello world

And now it works? That's interesting.

Questions:

  • Why does the /nix/store-path not seem to work in the shebang?
  • Are there some limitations on shebang expressions on MacOS that I'm not aware of? For example a maximum length, or certain characters like . or - that are not permitted?

My system:

  • MacOS Big Sur 11.2.1 (20D74)
  • bash --version: GNU bash, version 4.4.23(1)-release (x86_64-apple-darwin17.7.0)

Edit: Executing ./hello.py from zsh instead of bash actually works (when using the nix-shebang). So that might hint at this being an issue specific to bash.


Solution

  • MacOS doesn't support shebangs pointing at scripts -- this causes an Exec format error.

    Both bash and zsh try, with varying degrees of success, to work around execve() failures. However, the better fix is to use upstream nixpkgs' ability -- as introduced at https://github.com/NixOS/nixpkgs/pull/93757 -- to generate a wrapper that doesn't trigger the bug.

    This wrapper will not look like:

    #!/nix/store/i46k148mi830riq4wxh49ki8qmq0731k-python3-3.9.2-env/bin/python3.9
    

    ...but instead will look more like:

    #!/nix/store/74shlfgb18717ixjlpivpxd7iqcyhyn5-bash-4.4-p23/bin/bash /nix/store/i46k148mi830riq4wxh49ki8qmq0731k-python3-3.9.2-env/bin/python3.9
    

    ...which, somewhat surprisingly, works as intended.