I'm trying to deploy from GitHub using I want to execute more than one command, in order of the array. The code I'm using now is included below.
async.series([
...
// Deploy from GitHub
function (callback) {
// Console shizzle:
console.log('');
console.log('Deploying...'.red.bold);
console.log();
console.log();
var deployFunctions = [
{
command: 'cd ' + envOptions.folder + ' && pwd',
log: false
},
{
command: 'pwd'
},
{
command: 'su ' + envOptions.user,
log: false
},
{
command: 'git pull'
},
{
command: 'chmod 0777 * -R',
log: false
}
];
async.eachSeries(deployFunctions, function (item, callback) {
deployment.ssh2.exec(item.command, function (err, stream) {
deployment.logExec(item);
stream.on('data', function (data, extended) {
console.log(data.toString().trim());
console.log();
});
function done() {
callback(err);
}
stream.on('exit', done);
stream.on('end', done);
});
}, function () {
callback();
});
},
...);
But, after I cd'ed to the right directory, it forgets where it was and starts all over again.
$ cd /some/folder && pwd
/some/folder
$ pwd
/root
#robertklep is correct about why your cd doesn't persist. Each command invokes a distinct shell instance which starts in its initial state. You could prefix each command with cd /home/jansenstok/domains/alcoholtesterwinkel.com/public_html/ && as a quick fix, but really you are setting yourself up for pain. What you want is a shell script with all the power of multiple lines as opposed to a list of individual disconnected commands.
Look at using ssh2's sftp function to transfer a complete shell script to the remote machine as step 1, execute it via exec (/bin/bash /tmp/your_deploy_script.sh) as step 2, and then delete the script as step 3.
I know this is a super old question, but I ran into this problem while trying to manage an ACE through my Node server. The answer didn't work for me, but several searches later led me to a wrapper that worked really well for me. Just wanted to share here because this was the top link in my Google search. It's called ssh2shell and can be found here: https://www.npmjs.com/package/ssh2shell
It's very simple to use, just pass an array of commands and they run one by one waiting for each command to complete before moving on to the next.
A practical example:
const client = new Client();
const cmds = [
'ls -lah \n',
'cd /mnt \n',
'pwd \n',
'ls -lah \n',
'exit \n',
];
client.on('ready', () => {
console.log('Client :: ready');
client.shell((err, stream) => {
stream.on('close', (code) => {
console.log('stream :: close\n', { code });
}).on('data', (myData) => {
console.log('stream :: data\n', myData.toString());
}).on('exit', (code) => {
console.log('stream :: exit\n', { code });
client.end();
}).on('error', (e) => {
console.log('stream :: error\n', { e });
rej(e);
});
for (let i = 0; i < cmds.length; i += 1) {
const cmd = cmds[i];
stream.write(`${cmd}`);
}
});
}).connect({
host: '127.0.0.1',
port: 22,
username: 'root',
password: 'root',
});
all the examples in the doc use stream.end() which caused the creation of a new session instead of using the current one.
You cooldn't use "shell" on your program because "Shell" command invokes a new terminal on the system and does your jop. You need to use "exec" command without not emitting "exit" . Default "exec" command emits "exit" command after the command which you gave has been executed.
Related
I am trying to get the lines a ('never ending') python script puts into stdout. But currently my code would only log something to the console when the python process exits. Is there a way I can get the 'live' output of the python script line by line?
spawn_child.js:
let execFile = require("child_process").execFile;
var child = execFile("python3", ["PATH_TO_FILE"]);
child.stdout.on("data", data=>{
console.log(data.toString());
});
child.stderr.on("data", data=>{
console.log(data.toString());
});
child.on("exit", code=>{
console.log("Child exited with code "+code);
});
The python file:
from time import sleep
while True:
sleep(3)
print("test")
Edit: It works when using a nodejs script instead of a python script
change python script to
import time
import sys
while True:
time.sleep(1)
print("test")
sys.stdout.flush()
and increase the buffer size of the child process
const child = execFile("python", ["./runner.py"], {
detached: true,
maxBuffer: 10 * 1024 * 1024 * 1024
});
or you can do it without the flushing to stdout with python-shell
const { PythonShell } = require('python-shell');
let pyshell = new PythonShell('runner.py');
pyshell.on('message', function (message) {
console.log(message);
});
pyshell.end(function (err, code, signal) {
if (err) throw err;
console.log('The exit code was: ' + code);
console.log('The exit signal was: ' + signal);
console.log('finished');
});
Use spawn instead of execFile, dont forget options shell and stdio.
const spawn = require("child_process").spawn;
const child = spawn("python3", ["file.py"], {shell: true, stdio: 'inherit'});
child.on('data', function(data) {
console.log(data);
});
child.on('close', function(code) {
console.log('Child process exited with exit code '+code);
});
You can also add cwd option.
Was trying to implement something similar inside a NextJS application and wanted live output from my python script and using python-shell had the same issue that it was only giving me output when the process existed and I ended up using node-pty instead which worked as expected:
import { spawn } from "node-pty"
const pyProcess = spawn("python", ["path/to/python/script"], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.cwd(),
});
pyProcess.on('data', function (data: { toString: () => any; }) {
console.log(data.toString());
});
pyProcess.on('exit', (code: any) => {
console.log(`child process exited with code ${code}`);
});
I have created a command line interface that makes an API call to Google Books & allows for a user to search books using a keyword. The search returns a list of five books including a book ID that can be used to save the record to a reading list (written in a local file).
I would like to create some integration tests, but I am unsure of how to 'invoke' the program using the bin file that I have set up. Eventually I would like to test that user input results in the correct response from the programme, but the first thing I would need to test is that the initial command runs the programme.
Any help is much appreciated!
package.json
{
...
"bin": {
"books-cli": "bin/books-cli"
},
...
}
bin/books-cli
#!/usr/bin/env node
require('../')()
index.js
module.exports = () => {
const args = minimist(process.argv.slice(2));
let command = args._[0] || 'help';
if (args.help || args.h) {
command = 'help';
}
if (args.version || args.v) {
command = 'version';
}
switch (command) {
case 'search':
require('./commands/search')(args);
break;
case 'save':
require('./commands/save')(args);
break;
case 'list':
require('./commands/list')(args);
break;
case 'help':
require('./commands/help')(args);
break;
case 'version':
require('./commands/version')(args);
break;
default:
console.error(
`Sorry, "${command}" is not a valid command. Please type 'books-cli help' to see the help menu.`
);
break;
}
};
You can get the name of the binary from your package.json and spawn a child_process and then write assertions
for example (not tested):
books-cli.spec.js
const { bin: { books-cli }} = require('package.json');
const { exec } = require('child_process');
describe('books-cli', () => {
describe('help', () => {
let error;
let result;
beforeAll(done => {
exec(`${books-cli} help`, (err, stdout, stderr) => {
error = stderr;
result = stdout;
// let jest know when the process finishes execution
// so all the tests below will be run with error and
// result populated
done();
});
});
it('should not result an error', () => {
expect(error).toEqual('.....');
});
it('should output helpful information', () => {
expect(result).toEqual('....');
});
// describe the rest of the commands
describe('search');
describe('save');
});
});
Is it possible to run cmd.exe and execute some command with Electron.js?
If yes then how can I do this?
In your main.js file, you can put the following code:
//Uses node.js process manager
const electron = require('electron');
const child_process = require('child_process');
const dialog = electron.dialog;
// This function will output the lines from the script
// and will return the full combined output
// as well as exit code when it's done (using the callback).
function run_script(command, args, callback) {
var child = child_process.spawn(command, args, {
encoding: 'utf8',
shell: true
});
// You can also use a variable to save the output for when the script closes later
child.on('error', (error) => {
dialog.showMessageBox({
title: 'Title',
type: 'warning',
message: 'Error occured.\r\n' + error
});
});
child.stdout.setEncoding('utf8');
child.stdout.on('data', (data) => {
//Here is the output
data=data.toString();
console.log(data);
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', (data) => {
// Return some data to the renderer process with the mainprocess-response ID
mainWindow.webContents.send('mainprocess-response', data);
//Here is the output from the command
console.log(data);
});
child.on('close', (code) => {
//Here you can get the exit code of the script
switch (code) {
case 0:
dialog.showMessageBox({
title: 'Title',
type: 'info',
message: 'End process.\r\n'
});
break;
}
});
if (typeof callback === 'function')
callback();
}
Now, you can execute arbitary command (the example is from windows command prompt, but the funtion is universal) by calling:
run_script("dir", ["/A /B /C"], null);
The parameters of your command are in fact an array ["/A /B /C"], and the last parameter is callback to be executed, you can provide null as parameter, if special callback function is not needed.
it is possible by using node child_process , You can use this function:
const exec = require('child_process').exec;
function execute(command, callback) {
exec(command, (error, stdout, stderr) => {
callback(stdout);
});
};
// call the function
execute('ping -c 4 0.0.0.0', (output) => {
console.log(output);
});
and there are many packages in npm for this topic to .
I wrote a Nodejs script which finds the last changed/modified file names.
For that reason, I am using find CLI command. I have one hidden file .change to compare other files to that one (modified times).
Here is a code below:
const es6dir = 'es6';
const path2dir = './htdocs/';
const exec = require("child_process").exec;
exec(`find ${path2dir + es6dir}/ -type f -newer .change`, (error, stdout) => {
if(error){
console.log(`Error: ${error}`);
return;
}
console.log(stdout);
//update .change modified timestamp
exec('touch -c .change');
}
Everything works fine if I run this command in Git Bash but if I use windows terminal it says an incorrect command.
Is there a simple way which will work for Linux and Windows terminal at the same time?
I would like to run this command in both platforms because some of the team members are working on Linux while others are using windows machines.
Consider using Node's built-in fs.Stats over platform specific commands or utilities. The fs module exposing fs.stat method returns the property mtime comprising last modified time in milliseconds.
Cross compatibility can be achieved through child processes or using fs.stat and fs.writeFile.
Stats returns an object like such:
Stats {
dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 5077219,
size: 11,
blocks: 8,
atimeMs: 1556271390822.264,
mtimeMs: 1556271389892.5886,
ctimeMs: 1556271389892.5886,
birthtimeMs: 1556270439285.706,
atime: 2019-04-26T09:36:30.822Z,
mtime: 2019-04-26T09:36:29.893Z,
ctime: 2019-04-26T09:36:29.893Z,
birthtime: 2019-04-26T09:20:39.286Z }
As suggested in comments and an answer, I agree this would be a better approach. Here is how you can approach creating a new file, and checking creation date.
const fs = require('fs');
// Directory
const PATH = './';
// Get file's stats
fs.stat(`./.change`, function(error, stats) {
if (error) { throw error; } // Throw if an error, file not found
let time = Date.now(); // Current Time
console.log(`Current .change: Created: `, stats['mtime']); // Created Time
// If current time > file creation time
if (time > stats['mtime']) {
// writeFile function with filename, content and callback function
fs.writeFile(`${PATH}/.change`, 'Inside File', function (error) {
if (error) { throw error; }
console.log('File is updated successfully.');
});
}
});
I'm trying to create a simple script to run a few node files, these files are just api's for a quote database , Authentication , and a twitch bot.
The Quote and Authentication work just find however they twitch bot does not it says its connected to the server but when it goes to execute any commands it gets a unhandled promise rejection or some such error
However when I run the twitch bot separately in its own terminal session and not from the script it works just fine with no thrown errors.
Why will it work from a separate session that I create but not from a child_process?
note: I have had the child_process start up a new shell and its still the same issue
The process script:
const exec = require('child_process').exec;
const spawn = require('child_process').spawn
// I think twtich bot process has an issue without how much
//data is being passed to stdout or stderr and I need
//to specify how much data is allowed
twitchBot = exec('node ./twitchbot-api/index.js',
function(error, stdout, stderr){
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if(error !== null){
console.log('exec error: ' + error);
}
})
twitchBot.stdout.on('data', (data) => {
console.log(`twitchBot stdout:\n${data}`);
});
//whenver I recieve an error from quoteDataBase it also displays in the parent process
twitchBot.stderr.on('data', (data) => {
console.error(`twitchBot stderr:\n${data}`);
});
//this throws an error the first time I try a command saying not connected to server , I think I need to do this as exec
// let twitchBot = spawn('node ./twitchbot-api/index.js',{
// stdio: 'inherit',
// shell: true,
// detached: true,
// })
// //twitchBot.unref();
// twitchBot.on('error', (error)=>{
// console.log(`the erorr ${error}`)
// })
//starts up a child exec process for my quotes database
let quoteDataBase = exec('node ./quotes-api/index.js',
function(error, stdout, stderr){
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if(error !== null){
console.log('exec error: ' + error);
}
})
//whatever the child exec process quoteDatabase pushs to the standard out (console) displays on the parent process
// aka the terminal I run apiStart from
quoteDataBase.stdout.on('data', (data) => {
console.log(`quoteDataBase stdout:\n${data}`);
});
//whenver I recieve an error from quoteDataBase it also displays in the parent process
quoteDataBase.stderr.on('data', (data) => {
console.error(`quoteDataBase stderr:\n${data}`);
});
//starts up a child process for my user Authentication
let AuthenDataBase = exec('node ./authen-api/index.js',
function(error, stdout, stderr){
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if(error !== null){
console.log('exec error: ' + error);
}
})
And then the twitch bot code:
const tmi = require('tmi.js')
const haikudos = require('haikudos')
require('dotenv').config()
require('es6-promise').polyfill();
require('isomorphic-fetch');
// Valid commands start with:
let commandPrefix = '!'
// Define configuration options:
let opts = {
identity: {
username: process.env.user,
password: process.env.pass
},
channels: [
"dshrops1"
]
}
// These are the commands the bot knows (defined below):
let knownCommands = { echo, haiku, quote }
// Function called when the "echo" command is issued:
function echo (target, context, params) {
// If there's something to echo:
if (params.length) {
// Join the params into a string:
const msg = params.join(' ')
// Send it back to the correct place:
sendMessage(target, context, msg)
} else { // Nothing to echo
console.log(`* Nothing to echo`)
}
}
// Function called when the "haiku" command is issued:
function haiku (target, context) {
// Generate a new haiku:
haikudos((newHaiku) => {
// Split it line-by-line:
newHaiku.split('\n').forEach((h) => {
// Send each line separately:
sendMessage(target, context, h)
})
})
}
async function quote (target, context){
//cant deploy this on AWS yet Untill I deploy my database api as well.
let quote = await fetch('http://localhost:3006/random').then(resp =>resp.text())
sendMessage(target,context,quote)
}
// Helper function to send the correct type of message:
function sendMessage (target, context, message) {
if (context['message-type'] === 'whisper') {
client.whisper(target, message)
} else {
client.say(target, message)
}
}
// Create a client with our options:
let client = new tmi.client(opts)
// Register our event handlers (defined below):
client.on('message', onMessageHandler)
client.on('connected', onConnectedHandler)
client.on('disconnected', onDisconnectedHandler)
// Connect to Twitch:
client.connect()
// Called every time a message comes in:
function onMessageHandler (target, context, msg, self) {
if (self) { return } // Ignore messages from the bot
// This isn't a command since it has no prefix:
if (msg.substr(0, 1) !== commandPrefix) {
console.log(`[${target} (${context['message-type']})] ${context.username}: ${msg}`)
return
}
// Split the message into individual words:
const parse = msg.slice(1).split(' ')
// The command name is the first (0th) one:
const commandName = parse[0]
// The rest (if any) are the parameters:
const params = parse.splice(1)
// If the command is known, let's execute it:
if (commandName in knownCommands) {
// Retrieve the function by its name:
const command = knownCommands[commandName]
// Then call the command with parameters:
command(target, context, params)
console.log(`* Executed ${commandName} command for ${context.username}`)
} else {
console.log(`* Unknown command ${commandName} from ${context.username}`)
}
}
// Called every time the bot connects to Twitch chat:
function onConnectedHandler (addr, port) {
console.log(`* Connected to ${addr}:${port}`)
}
// Called every time the bot disconnects from Twitch:
function onDisconnectedHandler (reason) {
console.log(`Womp womp, disconnected: ${reason}`)
process.exit(1)
}
Sorry About any formatting issues
Despite all the issues that might be wrong with the twitch bot it does work fine when I run it from a separate terminal I am curious as to why it does work from the script and what I can do.
notes: I figured it might have to do with the twitch api being a IRC.