What are the rules behind finding executables in shebang statements?
0
votes
0
answers
30
views
I want to create a Docker image that has the Python3 runtime, a Python script of my creation, update-alternatives configured to run
python3
when python
is invoked, and I want to make the Python script "executable" by adding a shebang line at the top.
Putting that all together looks like this:
# dockerfile
from ubuntu:24.04
run apt-get update \
&& apt-get install -y python3 \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 1
copy my_python_exe.py /usr/local/bin
#! python
# my_python_exe.py
def main():
print("Hello, world!")
if __name__ == "__main__":
main()
$ chmod +x ./my_python_exe.py
$
How I build the docker image:
$ docker build -t tmp .
[+] Building 3.9s (8/8) FINISHED docker:default
=> [internal] load build definition from dockerfile 0.1s
=> => transferring dockerfile: 242B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:24.04 0.1s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/ubuntu:24.04@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782 2.5s
=> => resolve docker.io/library/ubuntu:24.04@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782 2.5s
=> [internal] load build context 0.0s
=> => transferring context: 149B 0.0s
=> CACHED [2/3] RUN apt-get update && apt-get install -y python3 && update-alternatives --install /usr/bin/python python /usr/bin/python3 1 0.0s
=> [3/3] COPY my_python_exe.py /usr/local/bin 0.1s
=> exporting to image 0.6s
=> => exporting layers 0.3s
=> => exporting manifest sha256:6ba674667609ecb5557b74c26c1575de071aa7df447700347d10d8f01001b112 0.0s
=> => exporting config sha256:31817234dbeddca20209469f49e623143733bc2353e2630c500de16a51356ea6 0.0s
=> => exporting attestation manifest sha256:39c41491e0ad0229401709a3b32c411cd3bd6d552c59c7aec2afc2d12deea269 0.1s
=> => exporting manifest list sha256:849fd19a3455b4d7c4dae89f469680545e51e46156978a21222bdf6e30505e01 0.0s
=> => naming to docker.io/library/tmp:latest 0.0s
=> => unpacking to docker.io/library/tmp:latest 0.0s
How I run the docker container:
$ docker run --rm -it --user $(id -u):$(id -g) -v $HOME:$HOME -w $HOME -v /etc/passwd:/etc/passwd:ro tmp
stonethrow@a59e51ccedf0:~$
The path within the running docker container context:
stonethrow@0b6d6819f651:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
How I run my Python script and **the error** that I'm asking about:
stonethrow@0b6d6819f651:~$ my_python_exe.py
bash: /usr/local/bin/my_python_exe.py: cannot execute: required file not found
What is that "cannot execute: required file not found
" error? Naively, I feel like a reasonable trail of breadcrumbs has been left for the command python
to resolve to /usr/bin/python3
:
- Python3 has been installed
- I've configured update-alternatives to establish the symlink /usr/bin/python
to point to /usr/bin/python3
(which itself points to the executable /usr/bin/python3.12
itself)
- My Python script uses the shebang convention -- which, per my, understanding tells the invoking shell what executable to use to run the script -- to specify that python
should be used to execute the script.
- $PATH
includes /usr/bin/
, where the python
symlink exists.
**Why does that "cannot execute: required file not found
" error occur?**
---
What I have tried:
1. Explicitly running python my_python_exe.py
in the docker container context works:
stonethrow@0b6d6819f651:~$ python /usr/local/bin/my_python_exe.py
Hello, world!
2. Modifying the shebang statement from #! python
to #! /usr/bin/python3
works (for the following, all code/dockerfiles are the same as provided above, *except for* the shebang statement in the Python script:
#! /usr/bin/python3
# my_python_exe.py
...
stonethrow@25ac7da86227:~$ my_python_exe.py
Hello, world!
3. Modifying the shebang statement from #! python
to #! /usr/bin/python
works:
#! /usr/bin/python
# my_python_exe.py
...
stonethrow@25ac7da86227:~$ my_python_exe.py
Hello, world!
4. Modifying the shebang statement to #! /usr/bin/env python
works:
#! /usr/bin/env python
# my_python_exe.py
...
stonethrow@25ac7da86227:~$ my_python_exe.py
Hello, world!
5. Modifying the shebang statement to #! python3
enables the error:
#! python3
# my_python_exe.py
...
stonethrow@0fdb8af3f7d6:~$ my_python_exe.py
bash: /usr/local/bin/my_python_exe.py: cannot execute: required file not found
6. Modifying the shebang statement to #! python3.12
enables the error:
#! python3.12
# my_python_exe.py
...
stonethrow@0fdb8af3f7d6:~$ my_python_exe.py
bash: /usr/local/bin/my_python_exe.py: cannot execute: required file not found
7. Modifying the shebang statement to #! env python
enables the error (and so does #! env python3
and #! env python3.12
):
#! /usr/bin/env python
# my_python_exe.py
...
stonethrow@0fdb8af3f7d6:~$ my_python_exe.py
bash: /usr/local/bin/my_python_exe.py: cannot execute: required file not found
---
The pattern from what I've tried implies that $PATH
isn't used while trying to run the executable referenced in shebang statements and/or you need to use the executable env
for $PATH
to be used, but of course, since $PATH
itself isn't in effect, in order to use env
you have to specify its full path. But that's conjecture; I'd be grateful if someone could set me straight on the underlying rules at play here.
Asked by StoneThrow
(1937 rep)
Mar 30, 2025, 09:26 PM