3

After installing https://github.com/bats-core/bats-core via brew install bats-core in Sonoma, I created a bats bash script.

When the first line is #!/usr/bin/env bats, it runs fine.

But when the first line is #!/opt/homebrew/bin/bats, it errors with @test: command not found. Strangely, running /opt/homebrew/bin/bats example.bats runs fine.

How do I go about getting #!/opt/homebrew/bin/bats to work the same as when explicitly using /opt/homebrew/bin/bats on the command line?

0

1 Answer 1

3

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.
7
  • 1
    I think we had this question before, but couldn't find it. If anybody stumbles into it, please flag as duplicate.
    – nohillside
    Commented Aug 22 at 18:08
  • While your answer is correct and helpful, I think the main problem is, why /opt/homebrew/bin/bats example.bats does work. It should be a command not found, unless the OP did something funny to /opt/homebrew. Or is it a common setup to have a bin directory under homebrew? Commented Aug 23 at 6:22
  • @user1934428 Not sure I understand your question. /opt/homebrew/bin is the default bin directory for all homebrew-installed binaries on Apple Silicon (or rather for the symlinks pointing to …/Cellar/package/bin/binary).
    – nohillside
    Commented Aug 23 at 6:28
  • I don't have a /opt/homebrew/bin, although I installed a lot of files with brew. They all ended up in /usr/local/bin. That's why I was surprised that the OP had a bin directory there. Could the default installation directory depends on the version of homebrew, or maybe even on the MacOS version? Commented Aug 23 at 6:36
  • 1
    @user1934428 Homebrew installs to /opt/homebrew on Apple Silicon, and creates the required directories during the installation process.
    – nohillside
    Commented Aug 23 at 7:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .