How to pass arguments to an interpreter found by 'env'.
There is an old annoyance that, if you use
env in a bang path to search the script interpreter in the shell's path, you cannot pass any arguments to it. Instead, all the text after the call to
env is passed as one single argument, and
env tries to find this as the executable to invoke, which fails of course when arguments are present.
env is not the culprit here, but the very definition of how a bang path works (quoted from the
If the program is a file beginning with
#!, the remainder of the first line specifies an interpreter for the program.
The shell executes the specified interpreter on operating systems that do not handle this executable format themselves.
The arguments to the interpreter consist of a single optional argument following the interpreter name on the first line… (emphasis mine)
So what env gets to see in its
argv array when you write something like
#! /usr/bin/env python3 -I -S is
['/usr/bin/env', 'python3 -I -S']. And there is no
python3 -I -S anywhere to be found that could interpret your script. 😞
env command in coreutils 8.30 solves this (i.e. Debian Buster only so far, Ubuntu Bionic still has 8.28). The relevant change is introducing a split option (
-S), designed to handle that special case of getting all arguments mushed together into one.
In the example below, we want to pass the
-I -S options to Python on startup. They increase security of a script, by reducing the possible ways an attacker can insert their malicious code into your runtime environment, as you can see from the help text:
-I : isolate Python from the user's environment (implies -E and -s) -E : ignore PYTHON* environment variables (such as PYTHONPATH) -s : don't add user site directory to sys.path; also PYTHONNOUSERSITE -S : don't imply 'import site' on initialization
You can try the following yourself using
docker run --rm -it --entrypoint /bin/bash python:3-slim-buster:
$ cat >isolated <<'.' #!/usr/bin/env -S python3 -I -S import sys print('\n'.join(sys.path)) . $ chmod +x isolated $ ./isolated /usr/local/lib/python38.zip /usr/local/lib/python3.8 /usr/local/lib/python3.8/lib-dynload
Normally, the Python path would include both the current working directory (
/ in this case) as well as site packages (
However, we prevented their inclusion as a source of unanticipated code – and you can be a happy cat again. 😻