Universal Node.js shebang?

The best I have come up with is this "two-line shebang" that really is a polyglot (Bourne shell / Node.js) script:

#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"

console.log('Hello world!');

The first line is, obviously, a Bourne shell shebang. Node.js bypasses any shebang that it finds, so this is a valid javascript file as far as Node.js is concerned.

The second line calls the shell no-op : with the argument // and then executes nodejs or node with the name of this file as parameter. command -v is used instead of which for portability. The command substitution syntax $(...) isn't strictly Bourne, so opt for backticks if you run this in the 1980s.

Node.js just evaluates the string ':', which is like a no-op, and the rest of the line is parsed as a comment.

The rest of the file is just plain old javascript. The subshell quits after the exec on second line is completed, so the rest of the file is never read by the shell.

Thanks to xavierm02 for inspiration and all the commenters for additional information!


This is only a problem on Debian-based systems, where policy overcame sense.

I don't know when Fedora provided a binary called nodejs, but I never saw it. The package is called nodejs and it installs a binary called node.

Just use a symlink to apply common sense to your Debian-based systems, and then you can use a sane shebang. Other people will be using sane shebangs anyway, so you're going to need that symlink.

#!/usr/bin/env node

console.log("Spread the love.");

#!/bin/sh
//bin/false || `which node || which nodejs` << `tail -n +2 $0`
console.log('ok');

//bin/false is the same thing as /bin/false except that the second slash transforms it into a comment for node, and that's why it's here. Then, the right side of the first || gets evaluated. 'which node || which nodejs' with backquotes instead of quotes launches node and the << feeds it whatever is on the right. I could've used a delimiter starting with // like dancek did, it would've worked but I find it cleaner to have only two lines at the beginning so I used tail -n +2 $0 to have the file read itself except the first two lines.

And if you run it in node, the first line is recognized as a shebang and ignored and the second is a one-line comment.

(Apparently, sed could be used to replace tail Print file content without the first and last lines)


Answer before edit:

#!/bin/sh
`which node || which nodejs` <<__HERE__
console.log('ok');
__HERE__

You can't do what you want so what you do instead is run a shell script, hence the #!/bin/sh. That shell script will get the path of the file needed to execute node, that is which node || which nodejs. The backquotes are here so that it gets executed, so 'which node || which nodejs' (with the backquotes instead of the quotes) simply calls node. Then, you just feed your script to it with <<. The __HERE__ are the delimiters of yours script. And the console.log('ok'); is an example of script that you should replace with your script.