I have a simple node.js app to echo stdin. When I run it interactively on the Windows console, I expected control-Z to be recognised as an EOF signal. But it isn't. So how do I get a node app to treat control-Z as EOF?
// testEcho.js
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?:",process.stdin.isTTY);
process.stdin.on('readable',function() {
var vText = process.stdin.read();
if (vText != null)
console.log('echo: "%s"',vText);
process.stdout.write('> '); // prompt for next
});
process.stdin.on('end',function() { // Works for redirected input but not triggered by ^Z on TTY
console.log('end of input reached');
});
```
the problem is you're using process.stdin.on instead of process.on()
See the fix I made here and everything should be fine and dandy :) Enjoy!
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?:", process.stdin.isTTY);
process.stdin.on('readable',function() {
var vText = process.stdin.read();
if (vText != null)
console.log('echo: "%s"',vText);
process.stdout.write('> '); // prompt for next
});
process.on('SIGINT', function () {
console.log('Over and Out!');
process.exit(0);
});
Also I replaced 'end' with 'SIGINT' as that's the signal that is caught by CTRL+C
You can read about the signal events here: https://nodejs.org/api/process.html#process_signal_events
It would appear the solution is to use readline. This is more terminal-aware, and treats an interactive TTY ctrl-D as EOF, while also handling redirected input streams correctly. Also, being line oriented/aware, it conveniently strips newlines from the input strings.
var readline = require('readline');
process.stdin.setEncoding('utf-8');
console.log("input is a TTY?",process.stdin.isTTY);
var rl = readline.createInterface({input: process.stdin, output: process.stdout});
rl.setPrompt('> ');
rl.prompt();
rl.on('line' ,function(aText) { console.log('echo: "%s"',aText); rl.prompt(); });
rl.on('close',function() { console.log('input has closed'); /* ... */ });
Related
Say you want to run / debug a HackerRank solution locally on your WebStorm / IntelliJ Idea IDE for for macOS before submitting it. What are the needed steps, considering node.js is already installed in your machine?
Sample Hello.js file as below:
const fs = require('fs');
function processData(input) {
const ws = fs.createWriteStream(process.env.OUTPUT_PATH);
var i, result = "";
for (i = 0; i < parseInt(input); i++) {
result += 'Hello - ' + i + '\n';
}
ws.write(result + "\n");
ws.end();
}
process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
_input += input;
});
process.stdin.on("end", function () {
processData(_input);
});
On macOS Mojave the steps are:
On Preferences > Keymap, add a keyboard shortcut Ctrl+D to run the Other > Send EOF action; beware this may remove other actions associated to this shortcut, such as "Debug" (credits to this answer)
Add the Hello.js file to your project and open it in the editor
Enter Modify Run Configuration... dialog (Cmd+Shift+A`, type "modify ..." and select it)
make sure Node interpreter:, Working directory: and JavaScript file: are properly set
(if the solution writes output to a file) Set also the Environment variables: to OUTPUT_PATH=./hello.txt or as desired
Save it
Run the configuration. In the terminal pane that opens:
provide the needed input; e.g. 4, then Enter
press Ctrl+D to fire the "end" event
check the generated file hello.txt
You may want to use console.log() to help debugging; make sure it's commented out before submitting your solution back to HackerRank.
Run Configuration:
Run Tool Window:
Try this works on windows, you can run it using node here end event gets triggered when you hit ctrl+D just like hackerrank or Mac os
'use strict';
const { METHODS } = require('http');
const readline = require('readline')
process.stdin.resume();
process.stdin.setEncoding('utf-8');
readline.emitKeypressEvents(process.stdin);
let inputString = '';
let currentLine = 0;
process.stdin.setRawMode(false)
process.stdin.on('data', inputStdin => {
inputString += inputStdin;
});
process.stdin.on('keypress', (str, key) => {
if (key && key.ctrl && key.name == 'd'){
inputString = inputString.trim().split('\n').map(string => {
return string.trim();
})
main();
}
});
function readLine() {
return inputString[currentLine++];
}
function method() {
}
function main() {
const n = parseInt(readLine().trim());
const arr = readLine().replace(/\s+$/g, '').split(' ').map(qTemp =>parseInt(qTemp,10))
method();
}
I'm trying to use mock-cli to stub process.arv in mocha tests for a cli app. I want to test that a message is console.logged when an incorrect argument ("imit") is passed to process.argv (as defined by commands).
I'm trying to adapt the example from the documentation but i don't think i have set everything up correctly.
it passes when i comment out "stdin: require('../mocks/fakeInputStream'), // Hook up a fake input stream" though i know it's not working correctly
it fails with TypeError: sourceStream.on is not a function when run as described below
Can someone see what I'm missing?
/index.js
var commands = ['init'];
function getGitHeadArgs() {
return process.argv.slice(2, process.argv.length);
}
if (getGitHeadArgs().length) {
if (!commands.includes(getGitHeadArgs()[0])) {
console.log("Silly Githead! That's not a githead command");
}
eval(getGitHeadArgs()[0])();
} else {
console.log("You didn't tell githead to do anything!");
}
/testIndex.js
var assert = require('assert');
var index = require('../index.js');
var mockCli = require("mock-cli");
describe("incorrect argument", function() {
it("imit throws an error if an invalid command is raised", function() {
var argv = ['node', '../index.js', 'imit']; // Fake argv
var stdio = {
stdin: require('../mocks/fakeInputStream'), // Hook up a fake input stream
stdout: process.stdout, // Display the captured output in the main console
stderr: process.stderr // Display the captured error output in the main console
};
var kill = mockCli(argv, stdio, function onProcessComplete(error, result) {
var exitCode = result.code; // Process exit code
var stdout = result.stdout; // UTF-8 string contents of process.stdout
var stderr = result.stderr; // UTF-8 string contents of process.stderr
assert.equal(exitCode, 0);
assert.equal(stdout, "Silly Githead! That's not a githead command\n");
assert.equal(stderr, '');
});
// Execute the CLI task
require('../index.js');
// Kill the task if still running after one second
setTimeout(kill, 1000);
});
Is ../mocks/fakeInputStream a valid path?
Is the object at ../mocks/fakeInputStream a valid instance of ReadableStream?
The source code is avalible at GitHub.
Make sure you meet the requirements for the captureStdin(sourceStream, callback) function.
The module uses that function to capture your fakeInputStream and pipe it into a captureStream.
I have the following code in index.js
var settingsFile = "config.json";
var settings = JSON.parse(require("fs").readFileSync(settingsFile));
const net = require('net');
const robot = require("robotjs");
const fs = require("fs");
var client;
var customKeys = {
"scroll_up":'robot.scrollMouse(50, "up");',
"scroll_down":"robot.scrollMouse(50,'down');"
}
function startCommunication(address,port) {
client = net.connect({port: port,host:address}, () => {
// 'connect' listener
console.log('connected to server!');
//client.write('world!\r\n');
});
client.on('data', (data) => {
console.log(data.toString());
var string = data.toString();
console.log(settings.keys[string.substr(1)]);
if(string.substr(0,1) == "d") {
robot.keyToggle(settings.keys[string.substr(1)],"down");
} else {
robot.keyToggle(settings.keys[string.substr(1)],"up");
}
//client.end();
});
client.on('end', () => {
console.log('disconnected from server');
});
}
startCommunication(settings.address,settings.port);
i also have this code in config.json, aka what is parsed into the settings variable.
{
"port":5555,
"address":"192.168.1.118",
"keys":{
"KEY_A":"a",
"KEY_B":"b",
"KEY_X":"x",
"KEY_Y":"y",
"KEY_L":"y",
"KEY_R":"t",
"KEY_DUP":"up",
"KEY_DDOWN":"down",
"KEY_DLEFT":"left",
"KEY_DRIGHT":"right",
"KEY_START":"z",
"KEY_SELECT":"q"
}
}
What The Problem is is that when i get down to either robotjs.togglekeys statement i get the error
Error: Invalid key code specified.
This means that, as there error clearly states, it is getting an invalid keycode. I am guessing that is is some stupid mistake that I made. data in my testing is equal to "dKEY_DRIGHT". the variable string is equal to that but i need to get rid of the d in order for it to work. when i do the live console i am able to get the data that I need by using the same code but somethig goes wrong when it is being ran in the file. anything helps :)
you may try using node-key-sender to send keys presses to your operational system.
Install it with npm install --save-dev node-key-sender.
And send a key to the keyboard using:
var ks = require('node-key-sender');
ks.sendKey('up');
All the values of you config ('a', 'b', ...) are accepted by the lib. You can send them directly.
Check the documentation page for more information: https://www.npmjs.com/package/node-key-sender.
So I'm using this example code from the NodeJS docs:
const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);
console.log("Hello!");
rl.setPrompt('OHAI> ');
rl.prompt();
rl.on('line', (line) => {
switch(line.trim()) {
case 'hello':
console.log('world!');
break;
default:
console.log('Say what? I might have heard `' + line.trim() + '`');
break;
}
rl.prompt();
}).on('close', () => {
console.log('Have a great day!');
process.exit(0);
});
I'm on Windows, running NodeJS 6.0.0. When I run the file, it writes "Hello!", followed by the first "OHAI> ". So far so good. Now I try writing an arbitrary "asd". When I press enter, one of two things happens:
It prints:
asd
Say what? I might have heard 'asd'
OHAI>
It prints
Say what? I might have heard 'asd'
OHAI>
This only happens on the first input line. It seems to be completely random, however if, after I type node test.js, I press enter fast enough, sometimes I get a newline before the first "OHAI> " and it doesn't print my arbitrary input.
Is there something wrong with the example? Is it a bug in NodeJS? Even if it can't be fixed, I'd be relieved to know what causes it, since I've been pulling my hairs out for hours now.
This should solve the issue.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
I'm still not sure why this happens, but I've found a way around it. It's a bit ugly, but it does the job.
var rl;
function cli() {
const readline = require('readline');
rl = readline.createInterface(process.stdin, process.stdout);
rl.prompt();
rl.on('line', exec_cmd);
rl.on('close', () => {
console.log('And now we part company.');
process.exit(0);
});
}
function fixfunc() {
//get first input
var input = process.stdin.read();
if (input == null)
return;
input = input.toString("utf-8");
//No longer needed, so remove the listener
process.stdin.removeListener('readable', fixfunc);
process.stdin.resume();
//do whatever you want with the input
exec_cmd(input);
//Initialize readline and let it do its job
cli();
}
process.stdin.pause();
process.stdin.on('readable', fixfunc);
process.stdout.write("> Welcome, Commander!\n> ");
What this basically does, is pause stdin, get the first input, manually call the parsing function and then initalize readline and let it do its job.
I am using the readline module to create a command line interface (CLI) for an application in Node.js.
The problem is that I can not scroll up to view the past commands as I usually can in Terminal. My CLI is just a fixed window and if I print too much out to the screen, I lose information at the top and there is no way to scroll up to see it.
(I am running my program on Mac OSX Mavericks)
Thanks in advance.
Code Snippet:
var readline = require('readline');
var Cli = function () {
this.txtI = process.stdin;
this.txtO = process.stdout;
process.stdout.write('CLI initialized.');
this.rl = readline.createInterface({input: this.txtI, output: this.txtO });
this.rl.setPrompt('>>>');
this.rl.prompt();
this.rl.on('line', function(line) {
var input = line.toString().trim();
if (input) {
this.txtO.write('cmd: ' + input);
}
this.rl.prompt();
}.bind(this)).on('close', function() {
this.txtO.write('Have a great day!');
process.exit(0);
}.bind(this));
};
new Cli();
Save this file as snippet.js and run
node snippet.js
in terminal.
It probably is working, just readline is overwriting your line. Try outputting multiple lines:
process.stdout.write("1\n2\n3\n4\n5");
Readline is quite an awesome module. History is already there. As is the possibility to add completion. Try the snippet below.
var readline = require('readline');
function createCLI(opt) {
var rl = readline.createInterface({
input : opt.input,
output : opt.output,
terminal : opt.terminal || true,
completer : opt.completer ||
function asyncCompleter(linePartial, callback){
var completion = linePartial.split(/[ ]+/);
callback(null, [completion, linePartial]);
}
});
rl.on('line', function(line) {
if( !line.trim() ){ this.prompt(); }
else { this.write(line); }
}).on('close', function() {
this.output.write('\n Have a great day!');
process.exit(0);
}).setPrompt(' > ');
rl.output.write(' CLI initialized\n');
return rl;
}
var cli = createCLI({
input : process.stdin,
output : process.stdout
});
cli.prompt();