Node spawn stdout.on data delay - linux

I am checking for USB drive removal on linux. I am simply monitoring the output of a command line process with child_process.spawn. But for some reason the child's stdout data event doesn't emit until like 20 lines have been printed, which makes it unable to detect a removed drive. After removing the drive many times, it does finally go. But obviously that won't do.
Original:
var udevmonitor = require("child_process").spawn("udevadm", ["monitor", "--udev"]);
udevmonitor.stdout.on("data", function(data) {
return console.log(data.toString());
});
Pretty simple. So I figure it's an issue with the piping node is using internally. So instead of using the pipe, I figure I'll just use a simple passthrough stream. That could solve the problem and give me real-time output. That code is:
var stdout = new require('stream').PassThrough();
require("child_process").spawn("udevadm", ["monitor", "--udev"], { stdio: ['pipe', stdout, 'pipe'] });
stdout.on("data", function(data) {
console.log(data.toString());
});
But that gives me an error:
child_process.js:922 throw new TypeError('Incorrect value for stdio stream: ' + stdio);
The documentation says you can pass a stream in. I don't see what I'm doing wrong and stepping through the child_process source didn't help.
Can someone help? You can run this yourself, provided you're on Linux. Run the code and insert a USB drive. Perhaps you can run the command 'udevadm monitor --udev' in another terminal to see what happens. Remove and reinsert a few times and eventually node will print out.

mscdex, I love you. Changing the spawn command to
spawn("stdbuf", ["-oL", "-eL", "udevadm", "monitor", "--udev"]);
Did the trick. I really appreciate your help!

Related

Using the exec method in Node.js

I have been following a series of tutorials on Node.js. To demonstrate child processes using exec, I have been given the code below under the file of exec.js When I go to the command line for node, I type in
node exec.js
then nothing happens. Why would this be?
var exec = require("child_process").exec;
exec("open http://www.linkedin.com");
Your code works for me.
To diagnose and fix this, I would try:
In your terminal (not using Node), verify that open http://www.linkedin.com works at all. You should see your default web browser open to the appropriate URL. If that doesn't happen, then Node can't magically fix it. Most likely, you'll need to set the default browser.
Wrap the URL in single quotes just for good measure. This protects you in some cases where the URL contains certain reserved characters.
exec("open 'http://www.linkedin.com'");
Add a callback so you can see the command's output and verify it completed successfully.
exec("open 'http://www.linkedin.com'", function (err, stdout, stderr) {
console.log('err:', err);
console.log('stdout:', stdout);
console.log('stderr:', stderr);
});
The ideal solution is to use opn, a high-quality module that already exists for this exact purpose.
For Windows, use the start command instead of open (which is for Mac) as below:
exec("start http://www.linkedin.com");

executing long running powershell script in nodejs

In my nodejs project I need to detect when usb flash drive is plugged in/out. I chose to use powershell script and run it using child_process exec or spawn.
psscript.ps1
Write-Host "listening for Win32_VolumeChangeEvent";
Register-WmiEvent -Query "SELECT * FROM Win32_VolumeChangeEvent" -Action {Write-Host "something happened"} | Out-Null;
I'm not an expert in powershell but it should just listen on volumeChangeEvent and write something happened when it fires
Nodejs spawns it as child using following code
nodescript.js
var spawn = require('child_process').spawn;
var child = spawn('powershell.exe', ['psscript.ps1']);
child.stdout.on('data', function(data){
console.log('DATA:', data.toString());
});
child.stdout.on('close', function(data){
console.log('psscript closed');
});
It should catch anything on child's stdout and parse it. For demonstration purpose it just prints out with prepended string DATA:.
This is the log. nodescript spawns the psscript and prints its output with the DATA: as i wanted. The problem is that the psscript closes and nodejs exits as well so i will never receive event.
C:\myproject>node nodescript.js
DATA: listening for Win32_VolumeChangeEvent
DATA:
psscript closed
C:\myproject>
so i was looking for a way to make the powershell not close. I ended up using -noexit argument in spawn var child = spawn('powershell.exe', ['-noexit', script]); which leads to this log.
C:\myproject>node nodescript.js
listening for Win32_VolumeChangeEvent
PS C:\myproject> something happened
something happened
nodescript spawns the psscript which prints the listening for Win32_VolumeChangeEvent just fine but then it prints PS C:\myproject> (notice the PS). I guess the powershell script registered event listener and then closed (hence the PS C:\myproject>) but powershell was still running (hence the something happened) as well as node. It's confusing fo me because I'm just used to the way node deals with event listeners (node never closes when any listenner is attached). Anyway the powershell script keeps on listening and notifying. That's the twice printed something happened because I plugged in and out the flash drive.
That is pretty much what I want but the problem is that the child spawned with -noexit takes over my nodejs console and pollutes it with output of the powershell while it never outputs it to the child.stdout and child.stdout.on('data', ...) never receives any data. As you can see in the second log there's no DATA:.
So I'm unable to work with it
Thank you for your help
To keep Node running - uou can just add a while(true) {} statement after your code snippet.
To have your node instance eventually timeout, you can use setTimeout and call process.exit() when timeout has expired.

Is there a way to continually keep a few blank lines below where the user inputs in a CLI (terminal) program?

I've a little node program that runs in terminal that asks users for input. After several user responses, the Make a selection: is pushed down to the bottom of the terminal.
Visually, I want to have maintain a few lines of space below where a user enters their choice.
I want the person to input as if they are in the center of the terminal.
Is there a technique to do this?
User input - note: read() is called repeatedly but not shown here:
var stdin = process.stdin
, stdout = process.stdout;
function option (answer) {
// do something with answer
};
function read () {
console.log('');
stdout.write(' \033[33m Make a selection" \033[39m');
stdin.resume();
stdin.setEncoding('utf8');
stdin.on('data', option);
};
Thanks.
Sorry if this is not a complete answer, but your one stop solution for making CLI programs with node.js is the commander npm package.

Execute shell command in foreground from node.js

I'm working on a Node.js CLI script that, as part of its duties, will sometimes need to take a large block of input text from the user. Right now, I'm just using the very basic readline.prompt(). I'd like some better editing capabilities. Rather than reinvent the wheel, I figure I could just do as crontab -e or visudo do and have the script launch a text editor that writes data to a temporary file, and read from that file once it exits. I've tried some things from the child_process library, but they all launch applications in the background, and don't give them control of stdin or the cursor. In my case, I need an application like vim or nano to take up the entire console while running. Is there a way to do this, or am I out of luck?
Note: This is for an internal project that will run on a single machine and whose source will likely never see the light of day. Hackish workarounds are welcome, assuming there's not an existing package to do what I need.
Have you set the stdio option of child_process.spawn to inherit?
This way, the child process will use same stdin and stdout as the top node process.
This code works for me (node v4.3.2):
'use strict';
var fs = require('fs');
var child_process = require('child_process');
var file = '/tmp/node-editor-test';
fs.writeFile(file, "Node Editor", function () {
var child = child_process.spawn('vim', [file], {stdio: 'inherit'});
child.on('close', function () {
fs.readFile(file, 'utf8', function (err, text) {
console.log('File content is now', text);
});
});
});

How do I get a basic setup of piping stdout to a file in Nodejs?

This seems basic enough, but I can't seem to get it to work.
var access = require( 'fs' ).createWriteStream( 'logs/test.access.log', { flags : 'a' } );
process.stdout.pipe( access );
I'm assuming that when I use console.log() after doing this, wouldn't that message be written to test.access.log? I don't get any errors, but I simply don't get anything in the file as well, so I'm wondering if someone can help me understand streams and writing from stdout to a log file like I have above.
Thanks!
Most likely, your Node.js app is closing before the stream's buffer is flushed to disk. This is probably because you're piping the process's own stdout into a file, and stdout normally doesn't close and flush until after Node.js starts to clean up the process.
Try sticking access.destroySoon(); into the end of your code and see if that causes it to flush the data to disk before ending the Javascript event loop.

Resources