Take the 2-minute tour ×
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 and Ubuntu 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. –  cadadr Feb 20 '13 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 '13 at 13:28

4 Answers 4

up vote 13 down vote accepted

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!

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 '13 at 12:15
    
Thanks! Very clever! I tested on Windows, ubuntu and OSX and it works. I just added "$@" after "$0" to pass the command line arguments to the subprocess. –  Bruno Jouhier Sep 5 '13 at 7:58
    
@BrunoJouhier good point about arguments! I'll add that. –  dancek Sep 5 '13 at 9:19
1  
Well, POSIX sh is ubiquitous these days (except Solaris /bin/sh – but Solaris does come with AT&T ksh out of the box, which is POSIX compatible), and Markus Kuhn recommends (with justification) to stay off it. IMHO you never need it. But yes, it's enough for mksh, bash, dash and other shells in modern Unix and GNU systems. –  mirabilos Dec 29 '13 at 20:50
1  
Great solution; though even more portable would be to use #!/usr/bin/env sh instead of #!/bin/sh (per en.wikipedia.org/wiki/Shebang_%28Unix%29#Portability) –  user7089 Feb 7 at 20:35
#!/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 '13 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 '13 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 '13 at 14:14
1  
//bin/false doesn't work on Mac OS X, either. I'm sorry, but this doesn't seem very portable anymore. –  dancek Feb 20 '13 at 8:09
2  
The which command is also not portable. –  jordanm Feb 20 '13 at 14:26

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.sh
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 '13 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. –  cadadr Feb 20 '13 at 15:44

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.");
share|improve this answer
    
I'm sorry, but this does not answer the question. I do understand the political point of view, but that is not the point here. –  dancek Aug 5 '13 at 8:58
    
For the record, some Fedora systems do have a nodejs executable, but that is not Fedora's fault. Sorry for misrepresenting the fact. –  dancek Aug 5 '13 at 9:01
4  
No need to apologise. You're quite right. My response doesn't answer your quesiton. It speaks to your question, suggesting that the question limits scope to less useful solutions than are available. I'm offering practical advice informed by history. Node is not the first interpreter to find itself here. You should have seen the commotion that led to Larry Wall declaring that the perl shebang MUST be #!/usr/bin/perl. That said, you aren't obliged to like or apply my suggestions. Peace. –  sheldonh Aug 6 '13 at 9:14

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.