Sample Header Ad - 728x90

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