How would I respond to a command line prompt programmatically with node.js? For example, if I do process.stdin.write('sudo ls'); The command line will prompt for a password. Is there an event for 'prompt?'
Also, how do I know when something like process.stdin.write('npm install') is complete?
I'd like to use this to make file edits (needed to stage my app), deploy to my server, and reverse those file edits (needed for eventually deploying to production).
Any help would rock!
You'll want to use child_process.exec() to do this rather than writing the command to stdin.
var sys = require('sys'),
exec = require('child_process').exec;
// execute the 'sudo ls' command with a callback function
exec('sudo ls', function(error, stdout, stderr){
if (!error) {
// print the output
sys.puts(stdout);
} else {
// handle error
}
});
For the npm install one you might be better off with child_process.spawn() which will let you attach an event listener to run when the process exits. You could do the following:
var spawn = require('child_process').spawn;
// run 'npm' command with argument 'install'
// storing the process in variable npmInstall
var npmInstall = spawn('npm', ['install'], {
cwd: process.cwd(),
stdio: 'inherit'
});
// listen for the 'exit' event
// which fires when the process exits
npmInstall.on('exit', function(code, signal) {
if (code === 0) {
// process completed successfully
} else {
// handle error
}
});
Related
For context, I'm on a Mac and I'm trying to script a 1Password CLI signin via their command-line tool. I'm trying to programmatically signing using a command that looks like:
op signin <signinaddress> <emailaddress> <secretkey> --output=raw
and I've tried with/without the --output=raw argument, but every time I simply get an error that looks like
[LOG] 2019/06/04 00:57:45 (ERROR) operation not supported on socket
child process exited with code 1
My initial hunch was that it had something to do with the command executions prompt displaying this special key character in the following image:
The relevant code is written in TypeScript and looks like this:
import { spawn } from 'child_process'
// ends up being `op signin <signinaddress> <emailaddress> <secretkey>`
const op = spawn(opExecutable, args);
let result: string | null = null
op.on('message', (message, sendHandle) => {
console.log('message', message, sendHandle)
});
op.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
if (data && typeof data.toString === 'function') {
result = data.toString()
}
});
op.on('close', (code, ...args) => {
console.log(`child process exited with code ${code}`, args);
});
Eventually, I'd like to run on all platforms and be able pass in stdin for the master password required to sign in, but I'm trying to figure out why my node app is crashing first :)
Apparently I was pretty close to a solution by using spawn, but I needed to specify configuration for stdio. Here's an example snippet of how I used spawn that worked for me:
const proc = spawn(
cmd, // the command you want to run, in my case `op`
args, // arguments you want to use with the above cmd `signin`, etc.
{
stdio: [
'inherit', // stdin: changed from the default `pipe`
'pipe', // stdout
'inherit' // stderr: changed from the default `pipe`
]
});
I am trying to execute a nodejs file called create-MySwitch.js through a child process in my main.js file.
this is the code for main.js
const exec = require('child_process').exec;
var cmdStr = 'node /home/pi/Desktop/lan-device/create-MySwitch.js';
exec(cmdStr,
{argv: {
port:8080,
uuid:'MyThing'
}},
(error, stdout, stderr)=>{
if (error) {
console.error('exec error: ${error}');
console.log(error);
return;
}
console.log('stdout: ${stdout}');
console.log('stderr: ${stderr}');
});
This is how I am attempting to access the arguments in the create-MySwitch.js file
var port_value = process.argv.port;
var uuid_value = process.argv.uuid;
The output is
stdout: ${stdout}
stderr: ${stderr}
when I run the command
node main.js
I do not think it's working because the output from executing the create-MySwitch.js file should be 'ready', but clearly is not being printed in the stdout variable from the child process.
Essentially, what I am trying to do is run the command 'node createMySwitch.js ', but instead of just typing this in to the command prompt I want to run that command using a child process.
I am doing all of this using the raspbian operating system.
I'd like to use the execSync method which was added in NodeJS 0.12 but still have the output in the console window from which i ran the Node script.
E.g. if I run a NodeJS script which has the following line I'd like to see the full output of the rsync command "live" inside the console:
require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"');
I understand that execSync returns the ouput of the command and that I could print that to the console after execution but this way I don't have "live" output...
You can pass the parent´s stdio to the child process if that´s what you want:
require('child_process').execSync(
'rsync -avAXz --info=progress2 "/src" "/dest"',
{stdio: 'inherit'}
);
You can simply use .toString().
var result = require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"').toString();
console.log(result);
Edit: Looking back on this, I've realised that it doesn't actually answer the specific question because it doesn't show the output to you 'live' — only once the command has finished running.
However, I'm leaving this answer here because I know quite a few people come across this question just looking for how to print the result of the command after execution.
Unless you redirect stdout and stderr as the accepted answer suggests, this is not possible with execSync or spawnSync. Without redirecting stdout and stderr those commands only return stdout and stderr when the command is completed.
To do this without redirecting stdout and stderr, you are going to need to use spawn to do this but it's pretty straight forward:
var spawn = require('child_process').spawn;
//kick off process of listing files
var child = spawn('ls', ['-l', '/']);
//spit stdout to screen
child.stdout.on('data', function (data) { process.stdout.write(data.toString()); });
//spit stderr to screen
child.stderr.on('data', function (data) { process.stdout.write(data.toString()); });
child.on('close', function (code) {
console.log("Finished with code " + code);
});
I used an ls command that recursively lists files so that you can test it quickly. Spawn takes as first argument the executable name you are trying to run and as it's second argument it takes an array of strings representing each parameter you want to pass to that executable.
However, if you are set on using execSync and can't redirect stdout or stderr for some reason, you can open up another terminal like xterm and pass it a command like so:
var execSync = require('child_process').execSync;
execSync("xterm -title RecursiveFileListing -e ls -latkR /");
This will allow you to see what your command is doing in the new terminal but still have the synchronous call.
Simply:
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
console.log(`Status Code: ${error.status} with '${error.message}'`;
}
Ref: https://stackoverflow.com/a/43077917/104085
// nodejs
var execSync = require('child_process').execSync;
// typescript
const { execSync } = require("child_process");
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
error.status; // 0 : successful exit, but here in exception it has to be greater than 0
error.message; // Holds the message you typically want.
error.stderr; // Holds the stderr output. Use `.toString()`.
error.stdout; // Holds the stdout output. Use `.toString()`.
}
When command runs successful:
Add {"encoding": "utf8"} in options.
execSync(`pwd`, {
encoding: "utf8"
})
How can I run a command line and get the outputs as soon as available to show them somewhere.
For example if a run ping command on a linux system, it will never stop, now is it possible to get the responses while the command is still processing ?
Or let's take apt-get install command, what if i want to show the progress of the installation as it is running ?
Actually i'm using this function to execute command line and get outputs, but the function will not return until the command line ends, so if i run a ping command it will never return!
var sys = require('sys'),
exec = require('child_process').exec;
function getOutput(command,callback){
exec(
command,
(
function(){
return function(err,data,stderr){
callback(data);
}
}
)(callback)
);
}
Try using spawn instead of exec, then you can tap into the stream and listen to the data and end events.
var process = require('child_process');
var cmd = process.spawn(command);
cmd.stdout.on('data', function(output){
console.log(output.toString()):
});
cmd.on('close', function(){
console.log('Finished');
});
//Error handling
cmd.stderr.on('data', function(err){
console.log(err);
});
See the Node.js documentation for spawn here: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
How to exec continuously? e.g. ls after cd?
I tried
exec = require('child_process').exec;
exec('cd ~/',
function(){
exec('ls'),
function(err, stdout, stderr){
console.log(stdout); // this logs current dir but not ~/'s
}
}
)
exec('cd ~/').exec('ls', function(err, stdout, stderr){
console.log(stdout);
})//this also fails because first exec returns a ChildProcess Object but not itself.
It is not possible to do this because exec and spawn creates a new process. But there is a way to simulate this. You can start a process with exec and execute multiple commands in the same time:
In the command line if you want to execute 3 commands on the same line you would write:
cmd1 & cmd2 & cmd3
So, all 3 commands run in the same process and have access to the context modified by the previous executed commands.
Let's take your example, you want to execute cd ../ and after that to execute dir and to view the previous directory list.
In cmd you shoud write:
cd../ & dir
From node js you can start a process with exec and to tell it to start another node instance that will evaluate an inline script:
var exec = require('child_process').exec;
var script = "var exec = require('child_process').exec;exec('dir',function(e,d,er){console.log(d);});";
script = '"'+script+'"';//enclose the inline script with "" because it contains spaces
var cmd2 = 'node -e '+script;
var cd = exec('cd ../ &'+cmd2,function(err,stdout,strerr)
{
console.log(stdout);//this would work
})
If you just want to change the current directory you should check the documentation about it http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
You can use nodejs promisify and async/await:
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);
export default async function () {
const cpu = await exec('top -bn1');
const disk = await exec('df -h');
const memory = await exec('free -m');
const payload = {
cpu,
disk,
memory,
};
return payload
}
If you want to use cd first, better use process.chdir('~/'). Then single exec() will do the job.
You can call exec with cwd param like so:
exec('ls -a', {
cwd: '/Users/user'
}, (err, stdout) => {
if (err) {
console.log(err);
} else {
console.log(stdout);
}
})
But beware, cwd doesn't understand '~'. You can use process.env.HOME instead.