bats
is a shell script, but shebangs (#!
) can only point to binaries, not to shell scripts. The reason for this is that the execve()
system call (which reads a file to be executed) doesn't handle indirect execution, it either expects a binary or a file starting with #! /path/to/binary
.
This makes #!/usr/bin/env bats
the only way to make these scripts work.
From execve(2)
:
execve() transforms the calling process into a new process. The new process is
constructed from an ordinary file, whose name is pointed to by path, called the
new process file. This file is either an executable object file, or a file of
data for an interpreter. An executable object file consists of an identifying
header, followed by pages of data representing the initial program (text) and
initialized data pages. Additional pages may be specified by the header to be
initialized with zero data; see a.out(5).
An interpreter file begins with a line of the form:
#! interpreter [arg ...]
When an interpreter file is execve()'d, the system runs the specified interpreter.
If any optional args are specified, they become the first (second, ...) argument
to the interpreter. The name of the originally execve()'d file becomes the
subsequent argument; otherwise, the name of the originally execve()'d file is
the first argument. The original arguments to the invocation of the interpreter
are shifted over to become the final arguments. The zeroth argument, normally the
name of the execve()'d file, is left unchanged.