Pass node command line options to npm executable? - node.js

Does anyone know if it is possible to pass node command line options (e.g. --expose-gc) directly into an npm executable.
I Have a node module that builds an executable (See here). I want that executable to have access to global.gc(). In order to do this, you need to start your node process with the --expose-gc flag.
I could force users to wrap my executable around a node command but then why do i even need an executable. Thoughts?

Assume your executable file is called ex. First, make sure it's executable by doing chmod a+x ex at the command line. Next, make sure the ex file begins with a line like:
#! /usr/bin/env node --expose-gc
env(1) will find the node executable on your path, and run it with the given arguments, passing the contents of ex into stdin of that process because of the #! "scratchbang" at the beginning of the line.
Run your program with just ex, or ./bin/ex (e.g.), rather than node ex.

Related

#reboot crontab has no effect

I have this crontab #reboot "/home/pi/Desktop/TV Scraper 2.0/run.sh" set up and for whatever reason it doesn't seem to run the bash file on reboot.
Typing "/home/pi/Desktop/TV Scraper 2.0/run.sh" on the terminal actually runs the script, so I know it's correct.
This is what's inside run.sh just in case:
#!/bin/bash
cd "/home/pi/Desktop/TV Scraper 2.0"
node ./app.js
I've also tried using #reboot root sh "/home/pi/Desktop/TV Scraper 2.0/run.sh" as well, but it doesn't work either.
How can I move forward with this? My knowledge of Linux is very limited. All I need is to have some Node and Python3 scripts run on every reboot. On Windows that's such an easy task: I've tried CRON, rc.local and autostart, nothing works.
My guess is that node is not available via cronjob, since its containing directory is not in your PATH environment variable. When you execute the script manually, it's probably available via PATH.
An easy fix for this is to use the full path, which you can get by executing which node. The result should be something like /usr/bin/node. Then you can use that, instead of just node.
For debugging purpose you can also redirect stdout and stderr to a file, so the last line in your script would look like this:
/usr/bin/node ./app.js &>/tmp/cron-debug.log
If that doesn't fix it, i would rename the directory "TV Scraper 2.0" and replace the whitespace characters with something like an underscore. Directory and file names are less likely to cause problems if you avoid whitespaces.

Why bin/www file doesn't have extension in express-generator?

I always have this doubt and I don't know why the file inside the bin folder which create a simple http server doesn't have a .js extension.
Is there a reason behind this?
It's tradition on unix that executables don't have extension.
For example, on Linux and MacOS to list a directory you type:
ls
you don't type
ls.exe
Another example, to launch the Dropbox service on Linux you can type
dropbox
you don't type
dropbox.py
even though dropbox is just a text file containing Python code.
Unix (and also bash terminal on Windows) have a feature where if a file is marked as executable (using the chmod command) and the first line contains:
#!
.. then the shell (the program controlling the command line) will remove the first two characters (#!) and execute the rest of that first line. This is often called the shbang line (sh = shell, ! = bang).
Therefore, if you want to develop a command-line program in node.js all you need to do is start the file with #! /usr/bin/env node:
#! /usr/bin/env node
// ^
// |
// the 'env' command will find the correct install path of node
console.log('hello world');
Then use chmod to make the file executable:
chmod +x my-script.js
Of course, creating a program that ends in .js does not look "professional". For example you don't type gulp.js when you run gulp and you don't type npm.js when you run npm. So people follow tradition and make their executable scripts have no extension - it makes it harder for people to realise that you didn't write the program in C or assembly language.
Because www file is executable.
Open a console, go to the bin folder, and type ./www . The server will run.

Anyway to change the default exec call length?

I have a bash script and will have the first line start with # and followed by the command to execute the script, and it seems the limitation is 80 characters due to the exec call has such limitation, is there anyway to change that ? because sometimes my path will be very long.
Update.
My case is that I use virtualenv to generate a clean python environment. And in this environment, there's one executable file called pip, the shebang line is python executable path and sometimes this path will be very long, e.g.
#!/Users/myname/github/myproject/virtualenv_python3.4/bin/python3.4
If you don't want to modify your path to include the directory in which the executable, you can create a simple wrapper:
#!/bin/bash
/Users/myname/github/myproject/virtualenv_python3.4/bin/python3.4 <(cat <<"EOF"
# Python script goes here
EOF) "$#"

What exactly does "/usr/bin/env node" do at the beginning of node files?

I had seen this line #!/usr/bin/env node at the beginning of some examples in nodejs and I had googled without finding any topic that could answer the reason for that line.
The nature of the words makes search it not that easy.
I'd read some javascript and nodejs books recently and I didn't remember seeing it in any of them.
If you want an example, you could see the RabbitMQ official tutorial, they have it in almost all of their examples, here is one of them:
#!/usr/bin/env node
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(err, conn) {
conn.createChannel(function(err, ch) {
var ex = 'logs';
var msg = process.argv.slice(2).join(' ') || 'Hello World!';
ch.assertExchange(ex, 'fanout', {durable: false});
ch.publish(ex, '', new Buffer(msg));
console.log(" [x] Sent %s", msg);
});
setTimeout(function() { conn.close(); process.exit(0) }, 500);
});
Could someone explain me what is the meaning of this line?
What is the difference if I put or remove this line? In what cases do I need it?
#!/usr/bin/env node is an instance of a shebang line: the very first line in an executable plain-text file on Unix-like platforms that tells the system what interpreter to pass that file to for execution, via the command line following the magic #! prefix (called shebang).
Note: Windows does not support shebang lines, so they're effectively ignored there; on Windows it is solely a given file's filename extension that determines what executable will interpret it. However, you still need them in the context of npm.[1]
The following, general discussion of shebang lines is limited to Unix-like platforms:
In the following discussion I'll assume that the file containing source code for execution by Node.js is simply named file.
You NEED this line, if you want to invoke a Node.js source file directly, as an executable in its own right - this assumes that the file has been marked as executable with a command such as chmod +x ./file, which then allows you to invoke the file with, for instance, ./file, or, if it's located in one of the directories listed in the $PATH variable, simply as file.
Specifically, you need a shebang line to create CLIs based on Node.js source files as part of an npm package, with the CLI(s) to be installed by npm based on the value of the "bin" key in a package's package.json file; also see this answer for how that works with globally installed packages. Footnote [1] shows how this is handled on Windows.
You do NOT need this line to invoke a file explicitly via the node interpreter, e.g., node ./file
Optional background information:
#!/usr/bin/env <executableName> is a way of portably specifying an interpreter: in a nutshell, it says: execute <executableName> wherever you (first) find it among the directories listed in the $PATH variable (and implicitly pass it the path to the file at hand).
This accounts for the fact that a given interpreter may be installed in different locations across platforms, which is definitely the case with node, the Node.js binary.
By contrast, the location of the env utility itself can be relied upon to be in the same location across platforms, namely /usr/bin/env - and specifying the full path to an executable is required in a shebang line.
Note that POSIX utility env is being repurposed here to locate by filename and execute an executable in the $PATH.
The true purpose of env is to manage the environment for a command - see env's POSIX spec and Keith Thompson's helpful answer.
It's also worth noting that Node.js is making a syntax exception for shebang lines, given that they're not valid JavaScript code (# is not a comment character in JavaScript, unlike in POSIX-like shells and other interpreters).
[1] In the interest of cross-platform consistency, npm creates wrapper *.cmd files (batch files) on Windows when installing executables specified in a package's package.json file (via the "bin" property). Essentially, these wrapper batch files mimic Unix shebang functionality: they invoke the target file explicitly with the executable specified in the shebang line - thus, your scripts must include a shebang line even if you only ever intend to run them on Windows - see this answer of mine for details.
Since *.cmd files can be invoked without the .cmd extension, this makes for a seamless cross-platform experience: on both Windows and Unix you can effectively invoke an npm-installed CLI by its original, extension-less name.
Scripts that are to be executed by an interpreter normally have a shebang line at the top to tell the OS how to execute them.
If you have a script named foo whose first line is #!/bin/sh, the system will read that first line and execute the equivalent of /bin/sh foo. Because of this, most interpreters are set up to accept the name of a script file as a command-line argument.
The interpreter name following the #! has to be a full path; the OS won't search your $PATH to find the interpreter.
If you have a script to be executed by node, the obvious way to write the first line is:
#!/usr/bin/node
but that doesn't work if the node command isn't installed in /usr/bin.
A common workaround is to use the env command (which wasn't really intended for this purpose):
#!/usr/bin/env node
If your script is called foo, the OS will do the equivalent of
/usr/bin/env node foo
The env command executes another command whose name is given on its command line, passing any following arguments to that command. The reason it's used here is that env will search $PATH for the command. So if node is installed in /usr/local/bin/node, and you have /usr/local/bin in your $PATH, the env command will invoke /usr/local/bin/node foo.
The main purpose of the env command is to execute another command with a modified environment, adding or removing specified environment variables before running the command. But with no additional arguments, it just executes the command with an unchanged environment, which is all you need in this case.
There are some drawbacks to this approach. Most modern Unix-like systems have /usr/bin/env, but I worked on older systems where the env command was installed in a different directory. There might be limitations on additional arguments you can pass using this mechanism. If the user doesn't have the directory containing the node command in $PATH, or has some different command called node, then it could invoke the wrong command or not work at all.
Other approaches are:
Use a #! line that specifies the full path to the node command itself, updating the script as needed for different systems; or
Invoke the node command with your script as an argument.
See also this question (and my answer) for more discussion of the #!/usr/bin/env trick.
Incidentally, on my system (Linux Mint 17.2), it's installed as /usr/bin/nodejs. According to my notes, it changed from /usr/bin/node to /usr/bin/nodejs between Ubuntu 12.04 and 12.10. The #!/usr/bin/env trick won't help with that (unless you set up a symlink or something similar).
UPDATE: A comment by mtraceur says (reformatted):
A workaround for the nodejs vs node problem is to start the file with
the following six lines:
#!/bin/sh -
':' /*-
test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$#"
test2=$(node --version 2>&1) && exec node "$0" "$#"
exec printf '%s\n' "$test1" "$test2" 1>&2
*/
This will first try nodejs and then try node, and only
print the error messages if both of them are not found. An explanation
is out of scope of these comments, I'm just leaving it here in case it
helps anyone deal with the problem since this answer brought the
problem up.
I haven't used NodeJS lately. My hope is that the nodejs vs. node issue has been resolved in the years since I first posted this answer. On Ubuntu 18.04, the nodejs package installs /usr/bin/nodejs as a symlink to /usr/bin/node. On some earlier OS (Ubuntu or Linux Mint, I'm not sure which), there was a nodejs-legacy package that provided node as a symlink to nodejs. No guarantee that I have all the details right.
The exec system call of the Linux kernel understands shebangs (#!) natively
When you do on bash:
./something
on Linux, this calls the exec system call with the path ./something.
This line of the kernel gets called on the file passed to exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
It reads the very first bytes of the file, and compares them to #!.
If the comparison is true, then the rest of the line is parsed by the Linux kernel, which makes another exec call with:
executable: /usr/bin/env
first argument: node
second argument: script path
therefore equivalent to:
/usr/bin/env node /path/to/script.js
env is an executable that searches PATH to e.g. find /usr/bin/node, and then finally calls:
/usr/bin/node /path/to/script.js
The Node.js interpreter does see the #! line in the file, but it must be programmed to ignore that line even though # is not in general a valid comment character in Node (unlike many other languages such as Python where it is), see also: Pound Sign (#) As Comment Start In JavaScript?
And yes, you can make an infinite loop with:
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bash recognizes the error:
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#! just happens to be human readable, but that is not required.
If the file started with different bytes, then the exec system call would use a different handler. The other most important built-in handler is for ELF executable files: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 which checks for bytes 7f 45 4c 46 (which also happens to be human readable for .ELF). Let's confirm that by reading the 4 first bytes of /bin/ls, which is an ELF executable:
head -c 4 "$(which ls)" | hd
output:
00000000 7f 45 4c 46 |.ELF|
00000004
So when the kernel sees those bytes, it takes the ELF file, puts it into memory correctly, and starts a new process with it. See also: How does kernel get an executable binary file running under linux?
Finally, you can add your own shebang handlers with the binfmt_misc mechanism. For example, you can add a custom handler for .jar files. This mechanism even supports handlers by file extension. Another application is to transparently run executables of a different architecture with QEMU.
I don't think POSIX specifies shebangs however: https://unix.stackexchange.com/a/346214/32558 , although it does mention in on rationale sections, and in the form "if executable scripts are supported by the system something may happen". macOS and FreeBSD also seem to implement it however.
PATH search motivation
Likely, one big motivation for the existence of shebangs is the fact that in Linux, we often want to run commands from PATH just as:
basename-of-command
instead of:
/full/path/to/basename-of-command
But then, without the shebang mechanism, how would Linux know how to launch each type of file?
Hardcoding the extension in commands:
basename-of-command.js
or implementing PATH search on every interpreter:
node basename-of-command
would be a possibility, but this has the major problem that everything breaks if we ever decide to refactor the command into another language.
Shebangs solve this problem beautifully.
Short answer:
It is the path to the interpreter.
EDIT (Long Answer):
The reason there is no slash before "node" is because you can not always guarantee the reliability of #!/bin/ . The "/env" bit makes the program more cross-platform by running the script in a modified environment and more reliably being able to find the interpreter program.
You do not necessarily need it, but it is good to use to ensure portability (and professionalism)

I'm learning about shebangs. How do I make it work with node.js in a Mac terminal?

I have:
#!/usr/bin/env node
console.log("It works!");
I learned that env finds the node program and interprets it with node. I checked that env exists in /usr/bin.
When I call node itworks.js it works and outputs It works!. However, from what I understand, I should just be able to call itworks.js without node due to the shebang. But when I make this command it says -bash: itworks.js: command not found.
Could someone help me get the shebang to work?
First of all you need to make the file executable:
chmod +x itworks.js
Then you need to call it by specifying the path as well. Either:
/where/it/is/on/disk/itworks.js
or:
./itworks.js
The reason for :
-bash: itworks.js: command not found
is because bash looks for programs in directories in the PATH environment variable when you do not say where the file is - it does not look in the current directory unless you tell it.
You could update the PATH variable with the current directory shortcut ., but that can be a security risk, so most run the program like this:
./itworks.js
Of course if you put your scripts all in one directory then you could add that to PATH in one of your start-up files. For example, if you had a directory called bin in your home directory that held your scripts:
PATH=$PATH:"$HOME/bin"
You also need to add the execute permissions to the script:
chmod u+x itworks.js
The u indicates that we only give permission for the current user to execute this file. If we omit the u then anyone can run it.

Resources