[Node.js][TypeScript][Windows] Play with child process
Tags: typescript, node
Published: 2021-06-12 15:24:00

## Intro
When I call another software from Node.js, I use "child_process" like this.
* [[Windows][Node.js][TypeScript] Get installed printer driver names](https://dev.to/masanori_msl/windows-node-js-typescript-get-installed-printer-driver-names-bj2)

"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](https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html), they are based on "child_process.spawn()".

#### index.ts
```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();
```
#### 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
```

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
```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();
```
#### 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
```

## 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](https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_process_child_process_spawn_command_args_options), I should use "child_process.spawn()".

#### index.ts
```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();
```
#### 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
```

## child_process.fork()
"child_process.fork()" can use to controll child process from parent process.
[The example](https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_process_child_process_fork_modulepath_args_options) aborts the child process from parent proces.

#### index.ts
```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();
```
#### sample.js
```js
function start() {
    console.log('hello child sample');
}
start();
```
#### Results
```
hello child sample
```

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)
```

## Resources
* [Child process | Node.js v16.3.0 Documentation](https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html)


## 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
```

* [Is there any powershell command to get the port IP address for a printer - Microsoft TechNet](https://social.technet.microsoft.com/Forums/en-US/52f79aa6-0ab2-495d-83bf-849e4e991cf4/is-there-any-powershell-command-to-get-the-port-ip-address-for-a-printer)

I tried this code to get the IP address.

```ts
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());
    });
}
```

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.

```ts

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

* [Execute Powershell script from Node.js - Stack Overflow](https://stackoverflow.com/questions/10179114/execute-powershell-script-from-node-js)