$ node yourfile.js
By typing the name of the interpreter (
node), you are explicitly telling the shell how to run your script.
But that knowledge can be put inside the script itself so it can be run directly as if it was a binary:
This will work only if you have execution permission on that file (you can set that with
chmod u+x yourfile.js for example) and have set the right “shebang”.
Shebang or hashbang (
#!) is the first line of the file which tells the OS which interpreter to use. It typically looks like this:
#!/absolute/path/to/the/interpreter [optional params]
Shebang is a OS feature and can be used to run any interpreted language: Python, Perl, etc. For Node.js it can (but often doesn’t) look like this:
Node.js will happily ignore this as a comment only if it is the very first line of the file (it won’t work even if there’s an empty line or
//comment line before it). Browsers ignore it too (Chrome 74+, FF 67+).
Most people have a Node.js binary or symlink sitting at
/usr/bin/node. If Node.js is not at
/usr/bin/node, the OS will complain. For example bash would say
bad interpreter: No such file or directory script won't execute. But is there a way to tell the OS to run the script with Node.js no matter where it is installed?
#!node doesn’t work because shebang requires an absolute path.
Say hello to
env is primarily intended to run a command in a modified environment. The emphasis here being “a command” because
env is almost always at
/usr/bin/env while “a command” can be anything that’s on the
If instead of
/usr/bin/node we write
/usr/bin/env node, we’re telling OS to run
env will run
node will in turn execute the script.
The short answer
This is the most common shebang for Node scripts:
env has a few other tricks up its sleeve that we can use.
Pass parameters to Node.js
-S option to
env causes it to parse whatever comes after which opens up a new door: passing parameters to the command.
For example let’s say we would like to run node with a special flag to enable ESM modules when running the current file. We could use this shebang:
#!/usr/bin/env -S node --experimental-module
Another example: if we want to run another script before running the current script, we can use Node’s
#!/usr/bin/env -S node -r ./my/other/file.js
Or to open up the inspection port:
#!/usr/bin/env -S node --inspect
Please note that if you run the script like
node yourfile.js , Node.js will not try to parse arguments from the shebang. It’ll just ignore it. It is the kernel which uses the shebang prior to running the file in order to figure out how to run it.
Set environment variables
Remember we said
env can run a command in a modified environment? That’s actually where it gets its name and it’s very powerful. Let’s say we want our script to run in production mode. We could set the
NODE_ENV environment variable:
#!/usr/bin/env -S NODE_ENV=production node
Without that, the
NODE_ENV would be
undefined or whatever is set at the user’s terminal when running the script.
Node.js respects a bunch of environment variables. For example we can use the
NODE_OPTIONS to pass some CLI flags like this:
#!/usr/bin/env -S NODE_OPTIONS=--experimental-modules node
Start with an empty environment
If we wanted the script to run without access to any environment variable at the user’s terminal, we could run it with the
-i flag which stands for “ignore environment”:
#!/usr/bin/env -S -i node
-i, so we can also write it like this:
#!/usr/bin/env -S - node
Maybe we don’t want to clear all environment variables but block-list a few of them. An example is
DEBUG (if you’re using the popular debug package). Maybe we don’t want the users of the scripts to set the flag when running it as a script. Then we’d use the
-u flag which stands for unset environment variable.
#!/usr/bin/env -S -u=DEBUG - node
If the user runs the script as
DEBUG=* ./yourfile.js they cannot see any debugging info but you can still run it as
DEBUG=* node ./yourfile.js see the DEBUG output.
Lock the Node.js runtime version
Sometimes you want to lock the
node version that is used to run a script. Prior to NPM@3 we could use
engineStrict, but that feature is removed and now we can only set the
package.json which may or may not exist next to the script and depends on setting the
engine-strict config flag.
#!/usr/bin/env -S npx node@6
This may try download the requested version of Node upon running the script (so it won’t work without internet connection if that particular version of node does not exist in the NPX cache).
Run it with TypeScript
There’s no rule that says we have to run
ts-node are available globally (
npm -i g typescript ts-node), we could specify
ts-node as the interpreter:
And let it run the file as a TypeScript program.
In any of these examples, the file can have the
.js extension or whatever else you prefer. It can even be without extentions!
Have I missed something? Do you have any tip you’d like to add? Please let me know in the comments or reach out on twitter: @alexewerlof
- Usage of the
envcommand in Unix
- The history of shebang in Unix and shebang on wikipedia
- Usage of
chmodcommand in Unix and its man page
- Why use
envin shebang (stackexchange)?
- Appropriate Node.js shebang (stackoverflow)?
- How does shebang work in Unix (stackoverflow)?
- List of environment variables recognized by Node.js