5

I have a JS file (file.js) that I want to have executed as a command line shell script through nodejs (iojs actually); I'm using MINGW Git Bash on Windows.

The standard way to do it is to put the following shebang in the JS file:

#!/usr/bin/env node

However, I want to pass a cmd line flag to node to activate V8 harmony features, i.e. I want an equivalent of

node --harmony_arrow_functions file.js

when run just as

./file.js

without the need to pass the flag manually.

Using

#!/usr/bin/env node --harmony_arrow_functions

does not work.

After considering https://stackoverflow.com/a/8108970/245966 and https://stackoverflow.com/a/5735690/245966 I created the following solution:

$ ls
file.js nodeharmony.sh

$ cat nodeharmony.sh
#!/bin/sh
exec node --harmony_arrow_functions "$@"

$ cat file.js
#!/bin/sh nodeharmony.sh               # or ./nodeharmony.sh, doesn't matter
console.log("Hello world")

$ ./file.js    # this works fine
Hello world

But the problem is that when I execute it from another folder, the shell tries to find nodeharmony.sh in the current working directory, not the directory of file.js:

$ cd ..
$ ./subfolder/file.js
/bin/sh: ./nodeharmony.sh: No such file or directory

Is there a way to create a portable shebang in file.js such that I can run file.js from whatever folder, without resorting to having my custom interpreter (nodeharmony.sh) available in the PATH?

Edit:

The JS file has to remain a valid JavaScript file, so it can not have more than an initial #! line, i.e. when I change file.js to be

#!/bin/sh
exec $(dirname $0)/nodeharmony.sh "$0"
console.log("Hello world")

then the shell properly passes the dir name and argument but it fails on Node side since the file is not a valid JS code:

$ ./subfolder/file.js
d:\CODE\subfolder\file.js:2
exec $(dirname $0)/nodeharmony.sh $0
     ^
SyntaxError: Unexpected identifier
    at exports.runInThisContext (vm.js:54:16)
    ...

Edit 2:

I'd also like to keep the possibility to run my script through

./file.js

as well as

node --harmony_arrow_functions ./file.js

So the suggested hack to sed the file contents, delete header and pipe it to node in the shebang is not good in this context, since the latter execution will be impossible.

2

EDIT: (The original answer still responds to the stated question, but indeed makes file.js not a valid JS file). In order to allow the desired behavior, follows a shebang + 1st-line-combination that will feed the file.js through node skipping the two first lines, thus feeding it with just the JS code:

#!/bin/sh
sed '1,2d' $0 |node --harmony_arrow_functions; exit $?
/* Your JS code begins here */

--- Below the original answer ---

If you can count as certain your interpreter will be in the same directory of the interpreted file (which is the stated question), you can use $(dirname $0) to obtain its path.

Example:

#!/bin/sh
exec $(dirname $0)/nodeharmony.sh "$0" "$@"

In this case, when executing

$ cd ..
$ ./subfolder/file.js

The $(dirname $0) will resolve to ./subfolder, so exec will use ./subfolder/nodeharmony.sh as the interpreter.

  • I used $0 instead of $@ to pass the original file name, but anyway this solution does not work, see my edit – jakub.g Feb 1 '15 at 16:59
  • @jakub.g, ok. Just edited the answer with a solution that will skip the first two lines and feed the source to node using a pipe. – Marcelo Feb 1 '15 at 18:53
  • That's pretty hacky but it worked for me (sed '1,2d' "$0" | node --harmony_arrow_functions ; exit $?), +1, thanks :) – jakub.g Feb 1 '15 at 20:00
  • I just realized your hack is not bulletproof since it prohibits passing the parameters to the script. E.g. ./file.js -param, normally this sets process.argv[2] == "-param", but when piping the script text to the node interpreter, this gets lost. Making this work would probably need even more hacks, not sure I need it that much ;) – jakub.g Feb 1 '15 at 21:51
  • Don't know much about JS to write a stub and test. Just try adding a "$@" or a -- "$@" right after the --harmony_arrow_functions and let me know if it works. – Marcelo Feb 1 '15 at 22:13
1

You can produce a Node script which is also a valid shell script to do this:

#!/bin/sh
//usr/bin/env node --harmony_arrow_functions "$0" "$@"; exit $?

console.log("Hello world")

Saving this as file.js produces a script which can be run from anywhere. It relies on // being equivalent to /; I haven’t tested this on Windows...

  • Interesting but doesn't work on Windows, AFAIK // is used by Git Bash as the prefix for network shares (equivalent to \\ prefix in Windows). – jakub.g Feb 1 '15 at 20:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.