How does the so-called shebang or (hash-bang) work
When the OS is asked to load a program with the exec()
system call,
the OS needs to know something about the program it's loading. Ordinarily
it's a compiled executable (in the so-called ELF format) like what you
get when you invoke the compiler.
However, Unix has also traditionally provided a method to specify instead
a custom interpreter. If the first 2 bytes of an executable are #!
, then
Unix will interpret whatever follows this as the name of a program that
it will invoke instead. The name of the program - really a command line -
ends with a newline. To this command it adds the name of the program.
So if you say #!/usr/bin/env python3
at the beginning of your script
script.py
, you're really saying that if someone tries to exec() this
executable (it must have the +x bit, or executable bit, for that),
the OS should instead run /usr/bin/env python3 script.py
.
Now you need to learn what /usr/bin/env
does - it's a command that
allows you to manipulate the environment and then starts another program.
Here, we're not asking it to manipulate the environment at all (that would
be env VARIABLE=value ...
) - but just to start the python3
program.
The reason people recommend #!/usr/bin/env python3
over
#!/usr/bin/python3
is that the former will use the execvp
system call that respects the user's PATH. See also: How do
environment variables work in Unix, and how does PATH
work in
particular?
I'm getting /usr/bin/env: ‘python3\r’: No such file or directory
If you add #!/usr/bin/env python3
but you created the file with Windows
line endings (CRLF), then the first line of the file will be
/usr/bin/env python3<CR><LF>
where <CR>
is typically displayed using its string escape \r
.
This is a telltale sign that the file was processed by a program that
applied Windows line endings.
To fix, run dos2unix <file>
on the file.
To avoid, don't use programs that perform line ending conversions.
Set any file transfer programs to binary mode, or better, don't copy
files between Windows and Unix machines.
Git also has an option to convert on checkout and checkin; turn it off.
In your text editor, make sure it's not configured to save with Windows
line endings. (In VSCode, it'll say LF
in the bottom toolbar.)