Search code examples
node.jscmd

Node js: Executing command line (opening a file)


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?


Solution

  • 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'});