Tell me more ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Node.js is very popular these days and I've been writing some scripts on it. Unfortunately, compatibility is a problem. Officially, the Node.js interpreter is supposed to be called node, but Debian, Ubuntu and Fedora ship an executable called nodejs instead.

I want portable scripts that Node.js can work with in as many situations as possible. Assuming the filename is foo.js, I really want the script to run in two ways:

  1. ./foo.js runs the script if either node or nodejs is in $PATH.
  2. node foo.js also runs the script (assuming the interpreter is called node)

Note: The answers by xavierm02 and myself are two variations of a polyglot script. I'm still interested in a pure shebang solution, if such exists.

share|improve this question
I think there is no real solution to this, as one is allowed to name the executable arbitarily by the build systems. There is nothing that keeps you from naming the python interpreter alphacentauri, you just follow the conventions and name it python. I'd suggest either using standard name node for your script, or having a kind of make script that modifies the shebang. – gkya Feb 20 at 12:17
@G.Kayaalp Politics and conventions aside, there are a lot of Debian/Ubuntu/Fedora users, and I want to make scripts that work for them. I don't want to setup a build system for this (whoever builds shell scripts before running them?), nor do I want to support alphacentauri and such. If there's an executable called nodejs, you can be 99% sure it's Node.js. Why not support both nodejs and node? – dancek Feb 20 at 13:28

3 Answers

#!/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 echo a file 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.

share|improve this answer
an explanation would be nice – 0xC0000022L Feb 18 at 22:14
Better quote the here-document delimiter to avoid parameter expansion, command substitution and arithmetic expansion to be performed on the JavaScript code before execution: <<'__HERE__'. – manatwork Feb 19 at 9:02
This is getting interesting! Although //bin/false doesn't work in my MSYS environment, and I need quotes around the backticks as my node resides at C:\Program Files\.... Yes, I work in a horrible environment... – dancek Feb 19 at 14:14
@dancek Nice :) | I prefer /bin/false || to ':' //; thought because I understand what it does ^.^ – xavierm02 Feb 19 at 19:08
1  
The which command is also not portable. – jordanm Feb 20 at 14:26
show 2 more comments

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.

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 and jordanm for inspiration and information!

share|improve this answer
I've found a two-line one :) | I don't think one line is possible because apparently, you're not allowed to use -c for sh in the shebang. – xavierm02 Feb 19 at 12:15

If you won't mind creating a little .sh file, I've a little solution for you. You can create a little shell script to determine which node executable to use, and use this script in your shebang:

shebang.sh:

#!/bin/sh
`which node || which nodejs` $@

script.js:

#!./shebang.js
console.log('hello');

Mark both executable, and run ./script.js.

This way, you avoid polyglot scripting. I do not think using multiple shebang lines are possible, although it seems like a good idea.

Although this solves the problem the way you want, it seems that no-one cares about this. For example, uglifyjs and coffeescript uses #!/usr/bin/env node, npm uses a shell script as an entry point, which again calls the executable explicitly with name node. I'm an Ubuntu user, and I didn't know this as I always compile node. I'm considering to report this as a bug.

share|improve this answer
I'm quite sure you'd at least have to ask the user to chmod +x on the sh script... so you might as well ask them to set a variable giving the location to their node executable... – xavierm02 Feb 20 at 14:57
@xavierm02 One can release the script chmod +x'd. And I concur that a NODE variable is better than which node || which nodejs. But the inquirer wants to provide an out-of-the-box experience, although many major node projects just use #!/usr/bin/env node. – gkya Feb 20 at 15:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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