2

I like to run my python scripts directly in bash like so

$ ./script.py

But sometimes I forget the shebang line #!/usr/bin/env python3, and it has the potential to overwrite a file named np if the script has import numpy as np (and if you click a few times when the ImageMagick import command is run).

What can I do to avoid such accidental execution of python scripts as bash scripts? Is there a way (shell option or .bashrc scripts) to block bash from executing a script that has an extension ".py" as a bash script? Or indeed, block any execution of scripts that do not have a shebang line?

(This question was closed on StackOverflow because it is not as related to programming as the community liked.)

2 Answers 2

1

If I remember correctly, this fallback to execution as shell script is implemented in several places; glibc does it internally when any program uses execv(), but the shell itself may also do the same (I have not checked how bash handles this situation). At least for glibc, as far as I know there is no easy way to disable it (short of patching glibc).

My own approach is to remove the ImageMagick import command (which anyway has been replaced with the magick super-command in 7.x so it's supposed to be called as magick import), and to replace it with a custom ~/bin/import that pops up a silly warning and then kills the parent process instead of doing anything harmful.

#!/bin/bash
file=$(readlink /proc/$PPID/fd/254)
echo "You forgot #! in $file." >&2
kill $PPID
3
  • Hmm? $ ll /bin/import /usr/bin/import => l.. /bin/import -> /etc/alternatives/import* ...
    – Hannu
    Commented Apr 22 at 16:42
  • @Hannu: I have no idea what you're trying to say by that. Yes, debian has alternatives? Something? Commented Apr 23 at 4:18
  • Removing those links might give you a problem with alternatives.
    – Hannu
    Commented Apr 23 at 16:21
1

Possible approach

In Linux you can use binfmt_misc. See the documentation, it states:

First you must mount binfmt_misc:

mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc

but Debian or Ubuntu (and possibly other distros that use systemd) mount this automatically out of the box. If mount | grep binfmt_misc shows binfmt_misc mounted on /proc/sys/fs/binfmt_misc then it's already set up.

You can manually assign /usr/bin/python3 to the py "extension" by invoking:

echo ':py:E::py::/usr/bin/python3:' | sudo tee /proc/sys/fs/binfmt_misc/register

For completeness, this is how you manually unregister:

echo -1 | sudo tee /proc/sys/fs/binfmt_misc/py

To make the assignment survive reboots (if your Linux uses systemd-binfmt.service), create a py.conf file in /etc/binfmt.d/ with the following content:

# Run .py files with /usr/bin/python3
:py:E::py::/usr/bin/python3:

See man 5 binfmt.d for details.

My tests indicate the defined interpreter wins with the shebang, if any. In other words: after applying the above, a shebang in a *.py file will be irrelevant, /usr/bin/python3 will be used anyway.


Personal view

The approach works and you may even like it, I'm not a fan of it though. My reasons:

  1. If you sometimes forget the shebang, you can forget the py "extension" as well. Frankly, in my opinion you should forget it, because…

  2. "Extensions" for executable files are ugly. Imagine you have rewritten your tool.py in C or whatever; are you going to change the filename? If so, everything that uses tool.py must be updated. If the file was named tool from the beginning, then the world could start using the new release just like that, maybe even without realizing something has changed. Relying on magic bytes (like the shebang) makes this easy.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .