Node js: Executing command line (opening a file) - node.js

I am trying to open a file through the command line by using node. I am using child_process.spawn, here is the code
var
process = require('child_process'),
cmd = process.spawn('cmd', ['start','tmp.txt'], {cwd: 'C:\\Users\\testuser\\Node_dev'});
I am expecting the file tmp.txt located in the Node_dev folder to be opened but I am getting the error -
dir error { [Error: spawn ENOENT] code: 'ENONENT', errno: 'ENOENT', syscall: 'spawn'
What am I missing?
Also, what's the difference between child_process.spawn vs child_process.exec for this?

I don't have an explanation for your ENOENT error [update: turns out this was a red herring], but one problem with your command is that you launch cmd.exe without /c, meaning that the shell spawned will (a) stay open and (b) actually ignore the specified command.
As for how the child_process module's methods differ:
.execFile and .exec take a callback that reports a single, buffered result, whereas .spawn provides chunked output via events.
Only .exec passes the command to the platform's default shell, which makes it more convenient, but less efficient.
In your case, you don't care about output returned from the spawned command, and since you need to involve the shell anyway,.exec is the best choice.
On Windows, use of start comes with its own pitfalls:
It is not an executable, but a shell builtin (to put it in Unix terms) - thus, it must be invoked via cmd.exe.
If its 1st argument is a [double-quoted] value with embedded spaces, it is interpreted as a window title rather than a filename argument; thus, to robustly pass a filename argument, you must pass an empty window title as the 1st argument.
Here are invocations that should work on Windows - note that both launch the child process asynchronously and ignore any output from it - all they do is to tell cmd.exe to open the specified file as if a user had opened it in Explorer:
using .exec:
var cpm = require('child_process');
// With .exec, specify the entire shell command as the 1st argument - it is implicitly
// passed to cmd.exe.
// '""' as the 1st argument to `start` is an empty window title that ensures that any
// filename argument with embedded spaces isn't mistaken for a window title.
cpm.exec('start "" "tmp.txt"', {cwd: 'C:\\Users\\testuser\\Node_dev'});
using .execFile or .spawn:
// With .spawn or .execFile, specify `cmd` as the 1st argument, and the shell command
// tokens as an array passed as the 2nd argument.
// Note the /c, which ensures that cmd exits after having executed the specified
// command.
// '""' as the 1st argument to `start` is an empty window title that ensures that any
// filename argument with embedded spaces isn't mistaken for a window title.
cpm.spawn('cmd', [ '/c', 'start', '""', 'tmp.txt' ], {cwd: 'C:\\Users\\testuser\\Node_dev'});

There is a cross-platform module on npm called open that opens files using the OS's default handler that you could use as a guide or just use it as-is.
To do it manually with spawn on Windows though, you'd do something like:
var args = ['/s', '/c', 'start', '', 'tmp.txt'],
opts = { cwd: 'C:\\Users\\testuser\\Node_dev' },
child = child_process.spawn('cmd.exe', args, opts);
child_process.exec() is basically a higher-level wrapper for child_process.spawn() which buffers output and then passes the buffered stdout and stderr output to a callback.

I am using the same, here is my command to run and executable with arguments:
var exec = require('child_process').spawn;
let scanProcess = exec('cmd', [ '/c', 'start', '""', 'feature.exe --version' ], {cwd: 'command_folder'});
I am running this on Azure DevOps in a container running Windows OS.
The above command just doesn't come back, it hangs. Any pointers.

Related

How to pass git pathspec args using node spawn

I am trying to run the following shell command from within a Node application:
git grep MySearchString -- 'MyPathToSearchFor'
This command runs successfully using's node's child_process.exec:
exec("git grep MySearchString -- 'MyPathToSearchFor'") // succeeds!
However, I cannot get the command working using child_process.spawn, which requires arguments to be provided as an array of strings.
// Spawn works without the pathspec args:
spawn('git', ['grep', 'MySearchString']) // success! exits with code 0
// It fails when the pathspec args are provided:
spawn('git', ['grep', 'MySearchString', '--', "'MyPathToSearchFor'"]) // exits with code 1
spawn('git', ['grep', 'MySearchString', "-- 'MyPathToSearchFor'"]) // exits with code 128
How can I provide spawn the -- MyPathToSearchFor args? How should I translate the dash-dash and pathspec args into spawn parameters?
The problem seems related to quotes in the args, but I'm not sure how to handle those.
The problem seems related to quotes in the args, but I'm not sure how to handle those.
Don't include quotes when you use the array argument form, e.g. just
spawn('git', ['grep', 'MySearchString', '--', "MyPathToSearchFor"])
With "'MyPathToSearchFor'" this makes git look for a file/directory whose name contains a single quote at the start and end (which in theory could exist, but rarely is what you have).

Wildcards in node child process [duplicate]

I want to execute a command like "doSomething ./myfiles/*.csv" with spawn in node.js. I want to use spawn instead of exec, because it is some kind of watch process and I need the stdout output.
I tried this
var spawn = require('child_process').spawn;
spawn("doSomething", ["./myfiles/*.csv"]);
But then the wildcard *.csv will not interpreted.
Is it not possible to use wildcards when using spawn()? Are there other possibilities to solve this problem?
Thanks
Torben
The * is being expanded by the shell, and for child_process.spawn the arguments are coming through as strings so will never get properly expanded. It's a limitation of spawn. You could try child_process.exec instead, it will allow the shell to expand any wildcards properly:
var exec = require("child_process").exec;
var child = exec("doSomething ./myfiles/*.csv",function (err,stdout,stderr) {
// Handle result
});
If you really need to use spawn for some reason perhaps you could consider expanding the wildcard file pattern yourself in Node with a lib like node-glob before creating the child process?
Update
In the Joyent Node core code we can observe an approach for invoking an arbitrary command in a shell via spawn while retaining full shell wildcard expansion:
https://github.com/joyent/node/blob/937e2e351b2450cf1e9c4d8b3e1a4e2a2def58bb/lib/child_process.js#L589
And here's some pseudo code:
var child;
var cmd = "doSomething ./myfiles/*.csv";
if ('win32' === process.platform) {
child = spawn('cmd.exe', ['/s', '/c', '"' + cmd + '"'],{windowsVerbatimArguments:true} );
} else {
child = spawn('/bin/sh', ['-c', cmd]);
}
Here's the simplest solution:
spawn("doSomething", ["./myfiles/*.csv"], { shell: true });
As #JamieBirch suggested in his comment, the key is telling spawn() to use the shell ({ shell: true }, see the docs), so the wildcard is properly resolved.
What OS are you using? In Unix-family OSs (e.g. Linux, MacOS), programs expect the shell process to expand wildcard filename arguments and pass the expansion in argv[]. In Windows OSs, programs usually expect to have to expand wildcards themselves (though only if they're Windows-native programs; ported Unix-family programs may at most try to run the arguments through a compatibility layer).
Your syntax looks like it's for a Unix-family system. If so, then when you call spawn() you're bypassing shell expansion, and your child process is going to treat dots and asterisks in arguments literally. Try using sh child_process in place of child_process and see if you get better results.

Is it possible to use array version of Perls system function to return immediately?

Is it possible to use the array version of perls system command (i.e. the version that takes the first element as the command and the rest of the array as the cmd arguments) while also spawning a new process with Linux so that the system command returns immediately e.g. to run a command like:
mycmd arg1 arg2 &
I've tried using system( ('mycmd', 'arg1', 'arg2', '&') ) but it just interprets the ampersand literally as a third argument.
I know I can just pass the whole command as a scalar to system but I'm specifically wondering if it's possible to use the array version because the parameters for this command will originate from user options in a CGI script.
The & part of the shell command tells the shell to run the process in the background, so bypassing the shell by using the multi-arg form of system makes no sense.
Solution 1: Quote using String::ShellQuote.
use String:ShellQuote qw( shell_quote );
system(shell_quote('mycmd', 'arg1', 'arg2').' &');
Solution 2: Quote using shell interpolation.
system('sh', '-c', '"$#" &', 'sh', 'mycmd', 'arg1', 'arg2');
Solution 3: Launch the program in the background yourself.
use IPC::Open3 qw( open3 );
{
open(local *CHILD_IN, '<', '/dev/null') or die $!;
local $SIG{CHLD} = 'IGNORE';
open3(
'<&CHILD_IN', '>&STDOUT', '>&STDERR',
'mycmd', 'arg1', 'arg2',
);
}
Since you have no interest in the fate of the executed program you can use fork/exec. And you're on Linux which allows using $SIG{CHLD} = 'IGNORE' to avoid waiting on the child process.
sub background {
local $SIG{CHLD} = 'IGNORE';
# fork and then exec if we are the child
exec(#_) or die($!) unless fork;
}
background( 'mycmd', 'arg1', 'arg2' );

Make a script which accept command-line arguments

What is the correct syntax for running a Node.js script with command-line arguments on Linux or Mac?
To run the script with no arguments, I would simply use the command node stuff.js, but in this case, I'd like to run a script called stuff.js with the arguments "blah", "hee", "woohoo!".
See http://nodejs.org/docs/latest/api/process.html#process_process_argv
In summary you'll run it like
node stuff.js blah hee "whoohoo!"
Then your arguments are available in process.argv
If you want to do more sophisticated stuff, the following modules are really helpful:
yargs by Benjamin Coe
commander by TJ Holowaychuk
vorpal by David Caccavella
nopt by Isaac Schlueter
And for fun
cli-table by Guillermo Rauch
node-multimeter by substack
chalk by Sindre Sorhus
Nomnom is another possible solution.
This simple node module is also helpfull: command-line-args
It allows to define a simple definition:
const optionDefinitions = [
{ name: 'verbose', alias: 'v', type: Boolean },
{ name: 'src', type: String, multiple: true, defaultOption: true },
{ name: 'timeout', alias: 't', type: Number }
]
It validates your options and allows you to access them in a simple way.
The arguments are stored in
process.argv
and to pass the arguments in command line please check below example:
ex. in this example below i have used commander NPM Module.
var args = require('commander')
Options with commander are defined with the .option() method.
The example below parses args and options from process.argv, leaving remaining args as the program.args array which were not consumed by options.
here process.argv is An array containing the command line arguments. The first element will be 'node', the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments
after executing.
function list(val) {
return val.split(',');
}
args.version('0.11.2')
.option('-t, --tag [value]', 'tags to ignore', list, undefined)
.parse(process.argv);
here to take input from command-line, we have to execute .js file with -t and after that arguments separated by comma(,)incase of multiple arguments
ex. : node example.js -t tagname
here i have used list to process multiple command line arguments ,so that we can pass multiple command line arguments
ex. node example.js -t tagname1, tagname2
so after this , all input passed as command line arguments will be available in array named args, so can use this array for your purpose
and
you can read more about it from here:-
https://nodejs.org/docs/latest/api/process.html#process_process_argv
or you can make use of the following modules :
commander:-
https://www.npmjs.com/package/commander
yargs :-
https://www.npmjs.com/package/yargs
vorpal :-
https://www.npmjs.com/package/vorpal

Wildcards in child_process spawn()?

I want to execute a command like "doSomething ./myfiles/*.csv" with spawn in node.js. I want to use spawn instead of exec, because it is some kind of watch process and I need the stdout output.
I tried this
var spawn = require('child_process').spawn;
spawn("doSomething", ["./myfiles/*.csv"]);
But then the wildcard *.csv will not interpreted.
Is it not possible to use wildcards when using spawn()? Are there other possibilities to solve this problem?
Thanks
Torben
The * is being expanded by the shell, and for child_process.spawn the arguments are coming through as strings so will never get properly expanded. It's a limitation of spawn. You could try child_process.exec instead, it will allow the shell to expand any wildcards properly:
var exec = require("child_process").exec;
var child = exec("doSomething ./myfiles/*.csv",function (err,stdout,stderr) {
// Handle result
});
If you really need to use spawn for some reason perhaps you could consider expanding the wildcard file pattern yourself in Node with a lib like node-glob before creating the child process?
Update
In the Joyent Node core code we can observe an approach for invoking an arbitrary command in a shell via spawn while retaining full shell wildcard expansion:
https://github.com/joyent/node/blob/937e2e351b2450cf1e9c4d8b3e1a4e2a2def58bb/lib/child_process.js#L589
And here's some pseudo code:
var child;
var cmd = "doSomething ./myfiles/*.csv";
if ('win32' === process.platform) {
child = spawn('cmd.exe', ['/s', '/c', '"' + cmd + '"'],{windowsVerbatimArguments:true} );
} else {
child = spawn('/bin/sh', ['-c', cmd]);
}
Here's the simplest solution:
spawn("doSomething", ["./myfiles/*.csv"], { shell: true });
As #JamieBirch suggested in his comment, the key is telling spawn() to use the shell ({ shell: true }, see the docs), so the wildcard is properly resolved.
What OS are you using? In Unix-family OSs (e.g. Linux, MacOS), programs expect the shell process to expand wildcard filename arguments and pass the expansion in argv[]. In Windows OSs, programs usually expect to have to expand wildcards themselves (though only if they're Windows-native programs; ported Unix-family programs may at most try to run the arguments through a compatibility layer).
Your syntax looks like it's for a Unix-family system. If so, then when you call spawn() you're bypassing shell expansion, and your child process is going to treat dots and asterisks in arguments literally. Try using sh child_process in place of child_process and see if you get better results.

Resources