5

I have this at the top of a script

#!/usr/bin/env mocha

as most people know, this tells the OS which executable to use to execute the script.

However, my question is - how can we include more information to the mocha executable as to how to execute the script?

mocha takes optional arguments, so I would like to do something like this:

#!/usr/bin/env mocha --reporter=tap --output=foo

but I don't think this is allowed.

How can I give the mocha executable more information about how to run the file?

4 Answers 4

9

The shebang line is interpreted by the kernel and is not very flexible. On Linux, it's limited to a single argument: the syntax is #!, optional whitespace, path to the interpreter (not containing whitespace), optional whitespace, and optionally a single argument (which may contain whitespace except at the beginning). Furthermore the total size of the shebang line is limited to 128 bytes (BINPRM_BUF_SIZE constant in the kernel sources, used in load_script).

If you want to pass more than one argument, you need a workaround. If you're using #!/usr/bin/env for path expansion, then there's only room for the command name and no other argument.

The most obvious workaround is a wrapper script. Instead of having /path/to/my-script contain the mocha code, you put the mocha code in some other file /path/to/my-script.real and make /path/to/my-script a small shell script. Here's a sample wrapper that assumes that the real code is in a file with the same name as the script, plus .real at the end.

#!/bin/sh
exec mocha --reporter=tap --output=foo "$0.real" "$@"

With a shell wrapper, you can take the opportunity to do more complex things such as define environment variables, look for available interpreter versions, etc.

Using exec before the interpreter ensures that the mocha script will run in the same process as the shell wrapper. Without exec, depending on the shell, it might run as a subprocess, which matters e.g. if you want to send signals to the script.

Sometimes the wrapper script and the actual code can be in the same file, if you manage to write a polyglot — a file that is valid code in two different languages. Writing polyglots is not always easy (or even possible) but it has the advantage of not having to manage and deploy two separate files. Here's a JavaScript/shell polyglot where the shell part executes the JS interpreter on the file (assuming that the JS interpreter ignores the shebang line, there isn't much you can do if it doesn't):

#!/bin/sh
///bin/true; exec mocha --reporter=tap --output=foo "$0" "$@"
… (the rest is the JS code) …
1
  • 3
    Just as an example of the latter option: the Scala programming language has a "script mode" which is specifically designed for such a thing, in particular, the Scala execution engine ignores anything in between #! and !#, so you can have as complicated a shell / Perl / Python / Ruby script in there as you like to find and start a suitable JVM, locate the Scala installation, figure out appropriate arguments and configuration options for the JVM and the Scala compiler and so on, then just exec whatever command line you cobbled together, and Scala will ignore the script portion. Mar 25, 2017 at 11:59
3

As a follow-up to Gilles' answer, here is a self-contained wrapper. This is a mix of his two solutions. It has the advantages of the polyglot script mentioned by Gilles (one script to maintain and deploy) without its drawbacks (quoting Gilles: "Writing polyglots is not always easy or even possible").

It consists of adding just a few lines at the top of your script:

#!/bin/bash
exec mocha --reporter=tap --output=foo <(sed -n '/^#MOCHA_START#/,$ p' "$0")
#MOCHA_START#
....
....
(mocha script)
....
....

What follows the #MOCHA_START# line is your actual mocha script left untouched. What the three first lines do is extract you mocha script and call mocha with it along with the desired arguments.

However, you may have to force your text editor to interpret the file as a Mocha script and to colorize it accordingly, since it will probably be confused by the first line of your script which indicates this is a Bash script:

enter image description here

4
  • thanks, upvoted, it may work for some more desperate scenarios, not sure if I can use it though. Mar 25, 2017 at 0:45
  • 1
    @AlexanderMills There is nothing extreme in this solution. It's akin to the polyglot solution suggested by Gilles. It has its advantages but it's not a polyglot one in the strict sense.
    – xhienne
    Mar 25, 2017 at 11:22
  • This might work, but it's not pretty; I'd have to save the file as .js to get proper syntax highlighting, but then having the bash call then becomes slightly confusing, and my IDE will visually mangle the bash code - most people's editors will mangle either the bash code or JS code (or whatever language), depending on which file extension you choose. The reason why "it's not pretty" matters, is because I would have to impose this paradigm on the users of my library, which I don't think I want to do. This is not for my code, otherwise I would use this, but instead I need a prettier solution. Mar 25, 2017 at 18:39
  • 1
    Thanks for your feedback. I have updated the answer to explain the screenshot. This is an interesting side effect. I believe text editors may allow you to force a certain colorization but it may not be a solution for you. Another possible solution would be a deployment script, called by a Makefile or a similar tool, that would add the necessary bash header to your unaltered Mocha scripts.
    – xhienne
    Mar 27, 2017 at 9:31
1

One of the disadvantages of using #!/usr/bin/env shebang line is that we can't pass arguments.

However, you can pass arguments if you use the path to your interpreter:

#!/path/to/mocha --reporter=tap --output=foo

See also:

10
  • ahh I didn't know that, I thought arguments were forbidden/ignored in any case, but as you say apparently they are only forbidden if we use usr/bin/env Mar 24, 2017 at 23:46
  • On a Mac you can pass arguments, even with usr/bin/env, but on other *nix machines I guess you can't. Mar 24, 2017 at 23:47
  • it's honestly a real F-ing shame that we cannot use arguments with #!/usr/bin/env on most *nix machines. Mar 24, 2017 at 23:49
  • 3
    On Linux, you can only pass a single argument that way. Mar 24, 2017 at 23:58
  • 1
    unix.stackexchange.com/q/63979 Mar 24, 2017 at 23:59
0

The only way I currently know how to do this is with env variables, so in my hashbang I have this:

#!/usr/bin/env mocha

...And mocha, or whichever executable, will have to accept environment variables, instead of command line arguments, in order for it to work correctly.

This unfortunately, is very limiting, because many executables do not use env variables, so this makes things very difficult for libraries, which must invoke other libraries, etc.

You must log in to answer this question.

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