nodejs child_process spawned a subprocess, the subprocess not show console window - node.js

I write nodejs code, use child_process.spawn() method create a subprocess, but the subprocess does not show its console window, how to show it?
'use stricts';
let process = require('process');
let child_process = require('child_process');
var subprocess = child_process.spawn(
'CSharpProg.exe', [] ,
{
windowsHide: false
});
subprocess.stdout.on('data', d => console.log(d.toString('utf8')));
process.stdin.on('data', d => subprocess.stdin.write(d));
I expec the spawned subprocess can have its own console window shown.

In order to make subprocess show its own console window (on Windows environment), you need to set option shell and detached as true. The default value of option windowsHide is false, so it's not necessary to set it again.
I tested the following code and it works on Windows 10 with Node.js v10.16.0:
var subprocess = child_process.spawn('node', ['test2.js'], {
shell: true,
detached: true
});

Related

How to get full stdout from a node.js child_process that spawns another process recursively?

I'm trying to spawn a child process like this:
const { spawn } = require('child_process')
const child = spawn("python run.py", {
detached: true,
shell: true,
})
The problem is, the run.py then calls another process called child.py.
I noticed that when I listen to the child's stdout, it prints all the log messages in the python run.py process, but not the python child.py, which is called interanlly by run.py.
So my question is, how do you capture all the STDOUT from child processes recursively?
The intermediate run.py python determines where stdio from child.py goes.
Here's some JS to run the python, setting specific stdio options
const { spawn } = require('node:child_process')
console.log('python.js')
const child = spawn("./run.py", {
stdio: [ 'inherit', 'inherit', 'inherit', 'pipe' ]
})
child.stdio[3].on('data', (data) => {
console.log('fd3: %s', data)
})
The stdio option, inherit is used to pass through the node processes stdin, stdout, stderr. This setup is not specific to your question, but I've included it as an example of what run.py will do with it's child.
The 4th element pipe opens an extra channel as file descriptor 3 (After descriptor 0/1/2 are used for stdin/stdout/stderr.
The intermediate run.py python then chooses where stdio goes for its child.py processes.
#!/usr/local/bin/python3
from subprocess import Popen
from os import fdopen
print('run.py')
# `Popen` defaults to `inherit`, so stdout/stderr from child.py will
# be written to the `run.py` processes stdout/sterr
Popen(['./child.py'])
# Second child, the file descriptor created in node will be used for stdout
pipe = fdopen(3)
Popen(['./child.py'], stdout=pipe)
The child script outputs as normal.
#!/usr/local/bin/python3
print('child.py')
When run:
$ node python.js
python.js
run.py
child.py
fd3: child.py

Can't seem to figure out how to get the output of an process fired with spawn. stdout.on('data') not outputting for me

I am trying to setup some automation on a game server for the game Rust.
The game server itself is ran by running its executable file RustDedicated.exe with some arguments.
According to some googling and reading here on Stack Overflow I have made this script:
import config from "config";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
const GAMESERVERPATH: string = config.get("Environment.RustDedicatedPath");
const EXECUTABLE: string = config.get("Environment.ExecutableFile");
const GAMESERVERARGS: Array<string> = [
"-batchmode",
"+server.port", `${config.get("Server.port")}`,
"+server.level", `"${config.get("Server.level")}"`,
"+server.seed", `${config.get("Server.seed")}`,
"+server.worldsize", `${config.get("Server.worldsize")}`,
"+server.maxplayers", `${config.get("Server.maxplayers")}`,
"+server.hostname", `"${config.get("Server.hostname")}"`,
"+server.description", `"${config.get("Server.description")}"`,
"+server.headerimage", `"${config.get("Server.headerimage")}"`,
"+rcon.port", `${config.get("Rcon.port")}`,
"+rcon.password", `"${config.get("Rcon.password")}"`,
"+rcon.web", `${config.get("Rcon.web")}`
];
const gameServerProc : ChildProcessWithoutNullStreams = spawn(
GAMESERVERPATH+EXECUTABLE,
GAMESERVERARGS,
{
cwd: GAMESERVERPATH,
shell: true,
}
);
gameServerProc.stdout.on("data", (data) => {
console.log(`stdout:${data.toString()}`);
});
gameServerProc.stderr.on("data", (data) => {
console.log(`stderr:${data.toString()}`);
});
gameServerProc.on("error", (err) => {
console.log(`error:${err.message}`);
});
What is happening is that i can see the output of the executable in the terminal window, and the server is firing without errors, but it seems that stdout is not firing the on('data') event.
I never see stdout:.
See the screenshot below where i have Code open, the output is on the bottom right.
Why is my script failing to get the on('data') firing when the executable outputs?
This issue had nothing to do with the code, but rather the game engine Unity. An extra argument -logFile - had to be present for the executable to output to stdout.

How to start an interactive terminal window on mac using child_process library of node?

I want to execute a bash command - dotnet dev-certs https --trust in an interactive terminal through child_process library.
Since it asks for the user password, it has to be interactive terminal.
I've already tried using AppleScript for it, but the user experience is subpar since it tends to leave half-closed terminal windows around.
Edit - Added the code snippet that I am using to create a child_process.
import * as cp from 'child_process'
cp.spawn('dotnet dev-certs https --trust', {})
I've tried many many combinations of cp.spawn and cp.exec.
e.g. cp.spawn('...', { shell: true, stdio: 'ignore', detached: true }) etc.
cp.spawn actually creates a process, but it's not interactive and immediately terminates.
You have to filter the incoming data from stdout with stdout.on('data', data => {}) to find the shell request for user input. After you found that specific line you can simply send data to the shell via the stdin.write('any input \n')
import * as cp from 'child_process'
const ls = cp.spawn('dotnet dev-certs https --trust', {})
ls.stdout.on('data', function (data) {
const currentData = data.toString();
//check for password input notification:
if(currentData === "input credentials: ")
{
//send the password via stdin. \n does the trick over here.
ls.stdin.write('Password\n');
}
});

Is there a way to launch a terminal window (or cmd on Windows) and pass/run a command?

Question
Is it possible to do the following?
open a new cmd.exe or terminal (on MacOS / Linux) window
pass / run a command, e.g. cd <path>
Problem
I can open cmd by running this command:
"$electron.shell.openItem('cmd.exe')"
But shell.openItem doesn't allow to pass the arguments / commands.
I tried using child_process but I couldn't make it work at all, it doesn't open a new terminal window:
const { spawn, exec } = require('child_process');
spawn('C:/Windows/System32/cmd.exe');
I also tried running the following command, but still nothing:
spawn( 'cmd.exe', [ '/c', 'echo ASDASD' ], { stdio: [0, 1, 2] } )
The only possible solution that I see is to create a command.bat:
start cmd.exe /K "cd /D C:\test"
And then use openItem:
"$electron.shell.openItem('command.bat')"
But that would only work on Windows
Solution
I finally found a way to do it on Windows:
var child_process = require('child_process');
child_process.exec("start cmd.exe /K cd /D C:/test");
Notes
You have to add the word start to open a new command window
Instead of cd /D C:/test you can specify any other command, e.g. python
/D is to make sure it will change the current drive automatically, depending on the path specified
/K removes the initial message
Don't use execSync it will lock the app until the terminal (command
prompt) window is closed
As for MacOS, looks like it's possible to do with osascript
osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Here is a working example showing how to open a Terminal window at a specific path (~/Desktop for instance) on macOS, from a renderer script:
const { app } = require ('electron').remote;
const atPath = app.getPath ('desktop');
const { spawn } = require ('child_process');
let openTerminalAtPath = spawn ('open', [ '-a', 'Terminal', atPath ]);
openTerminalAtPath.on ('error', (err) => { console.log (err); });
It should be easy to adapt it to any selected atPath...
As for running other commands, I haven't found a way yet...
And here is the equivalent working code for Linux Mint Cinnamon or Ubuntu:
const { app } = require ('electron').remote;
const terminal = 'gnome-terminal';
const atPath = app.getPath ('desktop');
const { spawn } = require ('child_process');
let openTerminalAtPath = spawn (terminal, { cwd: atPath });
openTerminalAtPath.on ('error', (err) => { console.log (err); });
Please note that the name of the terminal application may be different, depending on the Linux flavor (for instance 'mate-terminal' on Linux Mint MATE), and also that the full path to the application can be explicitly defined, to be on the safe side:
const terminal = '/usr/bin/gnome-terminal';
HTH...

How can I redirect a jsnode child process output to a cmd prompt?

I have an appjs application that is built to be a GUI which allows the user to run whole bunch of other .exe applications. These other .exe applications are created on a mouse click by the 'spawn()' command. Some of the .exe programs require output on the command line, however the main application doesn't use a command prompt.
So basically, I want my child processes to pipe their stdout into a command prompt window. The command prompt window is not running before hand. I am new to jsnode and I am having trouble getting this to work.
Here is the code. The name of the application is getting passed into the function and I am constructing the string and then spawning the process.
var appName = this.getAttribute('app');
processStr = './' + appName + '.exe';
var spawn = require('child_process').spawn;
cmd = spawn(processStr, [], { cwd: './', env: process.env} );
Note, even if I change it to below I cannot get the command prompt window to show up.
cmd = spawn('c:/windows/system32/cmd.exe', [], { cwd: './', env: process.env} );
var spawn = require('child_process').spawn;
var child = spawn('echo', ['Hello world!']);
child.stdout.pipe(process.stdout)

Resources