DEV Community

loading...

[Node.js][TypeScript][Windows] Play with child process

Masui Masanori
Programmer, husband, father I love C#, TypeScript, etc.
Updated on ・4 min read

Intro

When I call another software from Node.js, I use "child_process" like this.

"child_process" has four kind of methods(exclude "Sync" mehtods).

  • exec
  • execFile
  • fork
  • spawn

Because I wanted to know how to use them properly, I will try them.

Environments

  • Node.js ver.16.3.0
  • ts-node ver.10.0.0
  • TypeScript ver.4.3.2

child_process.spawn()

According to the documents, they are based on "child_process.spawn()".

index.ts

import { exec, execFile, fork, spawn } from "child_process";
import path, { resolve } from "path/posix";

function start() {
    spawnSample('powershell',['node', '-v']);
}
function spawnSample(command: string, args: string[]) {
    const execProcess = spawn(command, args);
    console.log('spawn');
    console.log(execProcess.spawnfile);
    execProcess.on('spawn', () => {
        console.log('spawn on spawn');
    });
    execProcess.stdout.on('data', (data) => {
        console.log(`spawn stdout: ${data}`);
    });
    execProcess.stderr.on('data', (data) => {
        console.log(`spawn on error ${data}`);
    });
    execProcess.on('exit', (code, signal) => {
        console.log(`spawn on exit code: ${code} signal: ${signal}`);
    });
    execProcess.on('close', (code: number, args: any[])=> {
        console.log(`spawn on close code: ${code} args: ${args}`);
    });
}
start();
Enter fullscreen mode Exit fullscreen mode

Results

spawn
powershell
spawn on spawn
spawn stdout: v16.3.0

spawn on exit code: 0 signal: null
spawn on close code: 0 args: null
Enter fullscreen mode Exit fullscreen mode

One important thing is this sample keeps waiting until terminating the child process.
So if I change the arguments of "spawnSample" like "spawnSample('C:\Program Files (x86)\Notepad++\notepad++.exe', []);", I must terminate Notpad++ manually or the sample won't finish.

child_process.exec()

"child_process.exec()" use OS default terminal applications.
On Windows, it uses "C:\WINDOWS\system32\cmd.exe".

To set command-line arguments, I only can use single string value.

index.ts

...
function start() {
...
    execSample('node -v');
}
...
function execSample(args: string) {
    const execProcess = exec(args, { 'encoding': 'utf8' }, (error, stdout) => {
        console.log(`exec stdout: ${stdout} error: ${error}`);
    });
    console.log('exec spawn');
    console.log(execProcess.spawnfile);
    execProcess.on('spawn', () => {
        console.log('exec on spawn');
    });
    execProcess.on('error', (err) => {
        console.log(`exec on error:${err}`);
    });
    execProcess.on('exit', (code, signal) => {
        console.log(`exec on exit code: ${code} signal: ${signal}`);
    });
    execProcess.on('close', (code: number, args: any[])=> {
        console.log(`exec on close code: ${code} args: ${args}`);
    });
}
start();
Enter fullscreen mode Exit fullscreen mode

Results

exec spawn
C:\WINDOWS\system32\cmd.exe
exec on spawn
exec on exit code: 0 signal: null
exec stdout: v16.3.0
 error: null
exec on close code: 0 args: null
Enter fullscreen mode Exit fullscreen mode

child_process.execFile()

"child_process.execFile()" can use applications what are first arguments directly as same as "child_process.spawn()".

I think normally I shall use "child_process.execFile()", and when I want to separate calling the applications and callback methods with some reasons, I shall use "child_process.spawn()".

And when I use two or more applications together like the example, I should use "child_process.spawn()".

index.ts

...
function start() {
...
    execFileSample('powershell', ['node', '-v']);
}
...
function execFileSample(command: string, args: string[]) {
    const execProcess = execFile(command, args, { 'encoding': 'utf8' }, (error, stdout) => {
        console.log(`execFile stdout: ${stdout} error: ${error}`);
    });
    console.log('execFile spawn');
    console.log(execProcess.spawnfile);
    execProcess.on('on spawn', () => {
        console.log('execFile on spawn');
    });
    execProcess.on('exit', (code, signal) => {
        console.log(`execFile on exit code: ${code} signal: ${signal}`);
    });
    execProcess.on('error', (err) => {
        console.log(`execFile on error:${err}`);
    });
    execProcess.on('close', (code: number, args: any[])=> {
        console.log(`execFile on close code: ${code} args: ${args}`);
    });
}
start();
Enter fullscreen mode Exit fullscreen mode

Results

execFile spawn
powershell
execFile on exit code: 0 signal: null
execFile stdout: v16.3.0
 error: null
execFile on close code: 0 args: null
Enter fullscreen mode Exit fullscreen mode

child_process.fork()

"child_process.fork()" can use to controll child process from parent process.
The example aborts the child process from parent proces.

index.ts

...
function start() {
...
    forkSample();
}
...
function forkSample() {
    const controller = new AbortController();
    const { signal } = controller;
    const child = fork(path.join(__dirname, 'sample.js'), ['child'], { signal });
    child.on('error', (err: any) => {
        // This will be called with err being an AbortError if the controller aborts
    });
    setTimeout(() => controller.abort(), 5_000);
}
start();
Enter fullscreen mode Exit fullscreen mode

sample.js

function start() {
    console.log('hello child sample');
}
start();
Enter fullscreen mode Exit fullscreen mode

Results

hello child sample
Enter fullscreen mode Exit fullscreen mode

One important thing is I can't use this for every applications.
So if I change the first arguments of "child_process.fork()" like "fork("C:/Program Files (x86)/Notepad++/notepad++.exe", ['child'], { signal });", I will get a runtime exception.

C:\Program Files (x86)\Notepad++\notepad++.exe:1
MZ�


SyntaxError: Invalid or unexpected token
    at Object.compileFunction (node:vm:353:18)
    at wrapSafe (node:internal/modules/cjs/loader:1039:15)
    at Module._compile (node:internal/modules/cjs/loader:1073:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
    at Module.load (node:internal/modules/cjs/loader:989:32)
    at Function.Module._load (node:internal/modules/cjs/loader:829:14)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
    at main (C:\Users\example\OneDrive\Documents\workspace\process-sample\node_modules\ts-node\src\bin.ts:246:14)
    at Object.<anonymous> (C:\Users\example\OneDrive\Documents\workspace\process-sample\node_modules\ts-node\src\bin.ts:382:3)
    at Module._compile (node:internal/modules/cjs/loader:1109:14)
Enter fullscreen mode Exit fullscreen mode

Resources

Get IP address from printer driver names

I can get printer IP addresses from printer driver names by powershell command.

Get-Printer "{Printer Driver Name}"| select portname
Enter fullscreen mode Exit fullscreen mode

I tried this code to get the IP address.

function getIpAddress(printerDriver: string) {
    const printerProcess = spawn('powershell', ['Get-Printer', `"${printerDriver}"`]);
    const selectProcess = spawn('powershell', ['select', 'portname']);

    printerProcess.stdout.on('data', (data) => {
        selectProcess.stdin.write(data);
    });
    selectProcess.stdout.on('data', (data) => {
        console.log(data.toString());
    });
}
Enter fullscreen mode Exit fullscreen mode

But I got nothing.
Because "selectProcess" didn't wait for stdout of "printerProcess".

After all, I used "exec" and set all commands include the pipe at once.


function getIpAddress(printerDriver: string) {
    const printerProcess = exec(`Get-Printer "${printerDriver}"|select portname`, { shell: 'powershell.exe'}, (err, stdout) => {
        console.log(stdout);
    });
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)