I'm working on a small cli tool that can automatically deploy Google Home actions based on the projects that are setup in a directory.
Basically my script checks the directories and then asks which project to deploy. The actual command that should run is coming from Google's cli gactions
To run it with arguments I setup a spawned process in my node-script:
const { spawn } = require('child_process')
const child = spawn('./gactions', [
'update',
'--action-package',
'<PATH-TO-PACKAGE>',
'--project',
'<PROJECT-NAME>'
])
child.stdout.on('data', data => {
console.log(data)
}
However, the first time a project is deployed, the gactions cli will prompt for an authorization code. Running the code above, I can actually see the prompt, but the script won't proceed when actually entering that code.
I guess there must be some way in the child process to capture that input? Or isn't this possible at all?
Simply pipe all standard input from the parent process to the child and all output from the child to the parent.
The code below is a full wrapper around any shell command, with input/output/error redirection:
const { spawn } = require('child_process');
var child = spawn(command, args);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
process.stdin.pipe(child.stdin);
child.on('exit', () => process.exit())
Note that if you pipe stdout you don't need handle the data event anymore.
require( "child_process" ).spawnSync( "sh", [ "-c", "npm adduser" ], { stdio: "inherit", stdin: "inherit" } );
this will execute the command given as we normally do in terminal.
Related
I'm working with a temp file that's downloaded from a server. but when I ran it on macOS, it ran fine. and did what it was supposed to do. but when I ran it on windows, it keeps giving me an EBUSY error when trying to spawn the child.
I tried delaying the start of the file. tried to remove the chmod so it just runs on Linux and macOS. But still getting the Ebusy error. Am I doing something wrong?
Note:
I can launch the binary from another node instance outside. Like from a cmd. but launching it from the node instances that created it leads to a Ebusy error.
temp.open('', (err, info) => {
if (err) throw err;
console.log('File: ', info.path);
console.log('Filedescriptor: ', info.fd);
var data = fs.createWriteStream(info.path);
res.data.pipe(data);
data.on('close', async () => {
fs.chmodSync(info.path, 0o755);
await delay(1000);
var child = cp.spawn(info.path, [key, jwt], { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] });
Error:
Error: spawn EBUSY
at ChildProcess.spawn (node:internal/child_process:415:11)
at Object.spawn (node:child_process:707:9)
at WriteStream.<anonymous>
at WriteStream.emit (node:events:394:28)
at emitCloseNT (node:internal/streams/destroy:138:10)
at processTicksAndRejections (node:internal/process/task_queues:82:21) {
errno: -4082,
code: 'EBUSY',
syscall:
Edit:
I created a new module to try to spawn the child that way. it will be forked in the main process. but I'm still getting the same error in the fork. still the same error.
const cp = require('child_process')
const childpath = process.argv[2]
var argv = [];
for (let index = 3; index < process.argv.length; index++) {
const element = process.argv[index];
argv.push(element)
}
console.log(argv)
var child = cp.spawn(childpath, argv, {
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
})
child.on('message', (msg) => {
if (process.send) process.send(msg)
else process.emit('message', msg)
})
child.on('error', (msg) => {
console.log(msg)
process.emit('error', msg)
})
child.on('close', (msg) => {
process.exit(msg)
})
Update:
I've noticed that I cannot run the file until the process the created it is ended. meaning that the process that needs to use it is using it. but I cannot do the thing I want to do with it.EI spawn it
Update 2:
The next thing I tried was to create a symbolic link with the node. And still nothing but I noticed that the file doesn't become runnable until the main process is ended. which means that the process that I'm running has something to do with it. So I need to be able to unlike it from the process that's running. it seems like windows need to do some initialization after the file is made. and because it's still connected to the main process in some way it's not able to. Im guess this is why when I ended the process, the node icon showed up on the symlink and that's why I'm able to run it manually.
Final Update:
I'm gonna work on a file system module that acts like temp files but is just regular files. that the process is tracking. giving the function of temp files but not really. this will allow me to use the function of temp files but without the file being unable to be executed. It seems like making it a temp file made it so it could not be executed by the same process that created it. it seems like a windows file system thing and how they handle temp file permissions.
The way Temp files are handled in windows is different than macOS. to my best estimate, it seems like Temp files in windows are linked to the process that created them and aren't able to be changed or accessed by that process. The problem lies in trying to execute these temp files which windows has assigned to a process already making it unassignable. NodeJs needs to be able to access the file to be able to launch it and windows permission will not allow it until the main process is killed or ended. this unlinks the file from that process making it accessible to the rest of the system.
I have a fresh installation of Ubuntu 20.04.2 (run in a virtual machine on mac).
I've installed Node.js v16 (via official software channel) and observed an extremely weird issue. Child processes spawned with studio: 'pipe' do not emit any stdout or stderr data.
Steps to reproduce:
child.js
console.log("Some text");
const interval = setInterval(() => {
console.log("Other text")
}, 100)
setTimeout(() => clearInterval(interval), 1000);
main.js
const childProcess = require('child_process');
const child = childProcess.spawn('node', ['child.js'], { stdio: 'pipe'});
if (child.stdout) {
console.log("Attach std listeners")
child.stdout.on('data', data => console.log("Child stdout:", String(data)));
child.stderr.on('data', data => console.log("Child stderr:", String(data)));
}
child.on('close', (...args) => { console.log("Child closed", args); });
Running node test.js produces following in Ubuntu terminal for me:
Attach std listeners
CLOSED [ 0, null ]
(when I run it on macOS, I get as expected multiple Child stdout: Other text in output)
While if I change stdio to inherit in main script, the output from child process is exposed:
Some text
Other text
Other text
Other text
Other text
Other text
Other text
Other text
Other text
Other text
Child closed [ 0, null ]
It signals to me that the child process is run without issues but for some reason, no stdout and stderr are emitted by a child when its output is supposed to be piped.
The same issue occurs with other Node.js versions I've checked (v14, v17, v18nightly)
It appears the problem is when Node.js is installed via Snap dependency manager (so official Ubuntu Software installer).
Software is then sandboxed in read-only containers, with restricted access to other system parts, and as it shows that cannot work for Node.js
Solution: Do not install Node.js via Snap, install via apt instead
In this case, I am using Node.js ChildProcess. Let's say the application file (index.js, for example) is in folder1. This folder also has folder2, which is where the class file is. So, when I call spawn from folder1, the command's current directory is folder1. However, I can't do java ./folder2/MyFile.
Here's what I tried:
async function run(path){
let child = spawn('java', [path], {
stdio: [process.stdin, process.stdout, process.stderr] //for testing purposes
})
}
Using function run on ./folder2/MyFile returns:
Error: could not find or load main class ..folder2.MyFile
I assume this has something to do with java and classpath. I saw an answer involving setting the classpath to the target directory (folder2) but it didn't do anything.
In short, how can I run a .class file from a different directory?
You can use exec instead of spawn so you can use two commands with & symbol which the second command runs when the first one finish without fail.
I think this might work for you.
const exec = require('child_process').exec;
exec("cd ./folder2 & java MyFile", function(
error: string,
stdout: string,
stderr: string
) {
console.log(stdout);
console.log(error);
console.log(stderr);
});
I have a node.js server set up and would like to send something to the command prompt on start up (because I couldn't figure out how to do it when the server was already running).
Do I do this from the server.js file? And what would the code look like?
I currently have this argument, which works when I normally access the windows command prompt on my own:
C:\Users\path\python.exe C:\Users\path\test.py
Server.js:
const exec= require("child_process").exec;
const pythonProcess =
exec("C:/Users/myPath/python.exe",
["C:/Users/myPath/test.py"]);
Everything compiles, and the server starts, but there is no evidence that the python file has been run. (When it normally runs, there is a document saved on my desktop) Am I doing something wrong?
Thank You.
It sounds like you're wanting to spawn a python process from a node process. You probably want to use Node's child_process.spawn() instead of child_process.exec(). Node's child_process.exec() is
asynchronous, and
buffer stdout and stderr, so you won't see any of their output until the callback on the exec() is invoked when the child process exits... and since you haven't given it a callback, you'll never see it.
Try something like this:
const childProcess = require('child_process') ;
const python = '.../python.exe' ;
const argv = [
'.../test.py',
] ;
const child = childProcess.spawn( python, argv, {
detached: false,
stdio: [
'inherit', // inherit STDIN from the parent process
'inherit', // inherit STDOUT from the parent process
'inherit', // inherit STDERR from the parent process
],
shell: false,
}) ;
child.on('error', ( err ) => {
console.log(`${python}: ${err.stack}`) ;
}) ;
child.on('exit', (code, signal) => {
console.log(`${python}: exited with exit code ${code}, signal '${signal}'`) ;
}) ;
But if all you're trying to do here is kick off a python program... why don't you do that via a shell script (on Windows, either a "batch" file (*.cmd) file or a Powershell script (*.ps)?
Think of having an admin app which creates & manages child apps.
I m spawning another child_process ( using childProcess.spawn('node', 'somefile.js') ). And, I m able to set event listeners (for ex, exit) on the childProcess, so that I can log the output & will know on child app exit.
But when the admin app restarts, I want to listen for the events on the child process again, so that adminapp will know the child app is crashed.
But child_process module doesn't have any method to get the childProcess instance by 'pid' (or some other mechanism).
Can someone give pointers how to do this? Can we do this using forever-monitor or pm2-api?
Update:
Code I've to spawn the child process:
let userWorkProcess = childProcess.spawn('node', [ 'someapp.js' ],
{
detached: true,
stdio: [ 'ignore', logFile, logFile ],
cwd: __dirname + '/someapp'
}
);
userWorkProcess.unref();
userWorkProcess.on('exit', function () {
if (userWorkProcess.exitCode !== 0) {
// child process exited with some error
} else {
// child process exited without any error
}
});
Update #2:
Can we connect to the process? For example, I see some code like that in forever-monitor. Is it possible using socket/nssocket?
https://github.com/foreverjs/forever/blob/master/lib/forever.js#L199
https://github.com/foreverjs/forever/blob/master/lib/forever.js#L229