Redirect stdout to a truncated file with Node.js - node.js

I am trying to write a utility script with Node.js, and have to save the stdout of a command to a file. Is there a simple way to do something like command arg1 arg2 > output.txt with Node?
I am invoking the command with spawn() of the child_process module, like var command = spawn("command", [arg1, arg2]), but there seems to be no way to redirect the stdout to a file.
Thanks!

As far as I know you'll have to append to a file manually by attaching an event handler to stdout as outlined here
It would look something like
const { spawn } = require('child_process')
const fs = require('fs')
const cmd = spawn(...)
const appendToLog = data => fs.appendFileSync('my-log.log', `${data}\n`)
cmd.stdout.on('data', appendToLog)

Related

return a string from an async nodejs function called in bash

i'm calling an async nodejs function that uses prompts(https://www.npmjs.com/package/prompts)
basically, the user is presented options and after they select one, i want the selection outputted to a variable in bash. I cannot get this to work. it either hangs, or outputs everything since prompts is a user interface that uses stdout
//nodefunc.js
async run() {
await blahhhh;
return result; // text string
}
console.log(run());
// bash
x=$(node nodefunc.js)
echo $x
Unless you can ensure nothing else in the node script will print to stdout, you will need a different approach.
I'd suggest having the node script write to a temporary file, and have the bash script read the output from there.
Something like this perhaps:
const fs = require('fs');
const outputString = 'I am output';
fs.writeFileSync('/tmp/node_output.txt', outputString);
node nodefunc.js
# Assuming the node script ran succesfully, read the output file
x=$(</tmp/node_output.txt)
echo "$x"
# Optionally, cleanup the tmp file
rm /tmp/node_output.txt

node.js: Trouble using a systemcall to write a file to the /tmp directory

As an exercise, I'm trying to use a systemcall from node.js to write a small text file to the /tmp directory. Here is my code:
#!/bin/node
var child_process = require("child_process");
var send = "Hello, world!";
child_process.exec('cat - > /tmp/test1', { input: send });
The file actually gets created; but, no content is placed in it. Things just hang. Can someone please tell me what I'm missing?
Also, I'd really like to know how to do this synchronously.
Thanks for any input.
... doug
hm unless i forgot to rtm too, this code will just never work. There is no such input option for cp.exec.
But there is a stdio option, will let us open the expected stdio on the child.
child_process.exec('cat - > /tmp/test1', { stdio: 'pipe' });
see https://nodejs.org/api/child_process.html#child_process_options_stdio
stdios are not string, they are streams, which we can end / write / pipe / close / push etc
see https://nodejs.org/api/stream.html
Note that stdin is a writable, stdout / stderr are readable.
To write the stdin of cat you ll now consume the cp.stdin object and call for its end() method.
child_process.exec('cat - > /tmp/test1', { stdio: 'pipe' }).stdin.end('hello world');
Note that end method is a write followed by a termination of the stream, which is required to tell cat to quit.
To ensure this is working well, we should refactor it, to not send stdin to a file, instead pipe child.stdout to the process.stdout.
var child_process = require('child_process');
var cp = child_process.exec('cat -', { stdio: 'pipe' });
cp.stdin.end('hello world');
cp.stdout.pipe(process.stderr);
Note that process is a global.
I finally got my original approach to work. The big stumbling block is to know that the synchronous methods are only available in version 0.12 (and later) of node.js. Here is the code that I finally got to work:
#!/usr/local/n/versions/node/0.12.14/bin/node
var child_process = require('child_process');
var send = "Hello, world!"
child_process.execSync('cat - > /tmp/test1', { input : send }).toString();
Thanks to all for the help.
... doug

nodejs : how to log to screen AND to file?

I use console.log in my node.js: that way I can log to screen
ex:
node myscript.js
If I use
node myscript.js>log.txt then I log to file log.txt
How can I log to screen AND to file ?
Use tee.
node myscript.js | tee log.txt
If you want this behavior to be persistent within your app, you could create a through stream and pipe it to both a writeStream and stdout.
var util = require('util');
var fs = require('fs');
// Use the 'a' flag to append to the file instead of overwrite it.
var ws = fs.createWriteStream('/path/to/log', {flags: 'a'});
var through = require('through2');
// Create through stream.
var t = new through();
// Pipe its data to both stdout and our file write stream.
t.pipe(process.stdout);
t.pipe(ws);
// Monkey patch the console.log function to write to our through
// stream instead of stdout like default.
console.log = function () {
t.write(util.format.apply(this, arguments) + '\n');
};
Now this will write to both stdout (terminal display) and to your log file.
You can also omit the through stream and just write to both streams in the monkey patched function.
console.log = function () {
var text = util.format.apply(this, arguments) + '\n';
ws.write(text);
process.stdout.write(text);
};
The through stream just gives you a single stream you could utilize in other ways around your app and you'd always know that it was piped to both output streams. But if all you want is to monkey patch console.log then the latter example is sufficient :)
If you only want to do this for a single run of your app from the terminal, see #andars' answer and the tee command :)
PS - This is all that console.log actually does in node, in case you were wondering.
Console.prototype.log = function() {
this._stdout.write(util.format.apply(this, arguments) + '\n');
};

Move files with node.js

Let's say I have a file "/tmp/sample.txt" and I want to move it to "/var/www/mysite/sample.txt" which is in a different volume.
How can i move the file in node.js?
I read that fs.rename only works inside the same volume and util.pump is already deprecated.
What is the proper way to do it? I read about stream.pipe, but I couldn't get it to work. A simple sample code would be very helpful.
Use the mv module:
var mv = require('mv');
mv('source', 'dest', function(err) {
// handle the error
});
If on Windows and don't have 'mv' module, can we do like
var fs = require("fs"),
source = fs.createReadStream("c:/sample.txt"),
destination = fs.createWriteStream("d:/sample.txt");
source.pipe(destination, { end: false });
source.on("end", function(){
fs.unlinkSync("C:/move.txt");
});
The mv module, like jbowes stated, is probably the right way to go, but you can use the child process API and use the built-in OS tools as an alternative. If you're in Linux use the "mv" command. If you're in Windows, use the "move" command.
var exec = require('child_process').exec;
exec('mv /temp/sample.txt /var/www/mysite/sample.txt',
function(err, stdout, stderr) {
// stdout is a string containing the output of the command.
});
You can also use spawn if exec doesn't work properly.
var spawn = require("child_process").spawn;
var child = spawn("mv", ["data.csv","./done/"]);
child.stdout.on("end", function () {
return next(null,"finished")
});
Hope this helps you out.

how to tail multiple files in node.js?

when use the following code to tail a file, we can successfully output data.
var spawn = require('child_process').spawn;
var filename = '/logs/error.log';
var tail = spawn("tail", ["-f", filename]);
tail.stdout.on("data", function (data) {
console.log(data);
});
but when i change filename to "/logs/*.log", i don't find anything output. who can tell me what is the reason? Thanks!
When typing tail -f /logs/*.log on the console, the expansion of /logs/*.log is handled by the shell; by the time the tail program gets the arguments, they've already been expanded to tail -f /logs/error.log /logs/other.log. You need to do the expansion yourself for Node:
var fs = require('fs');
var spawn = require('child_process').spawn;
var filename = fs.readdirSync('/logs').map(function(file) { return '/logs/' + file });
var tail = spawn("tail", ["-f"].concat(filename));
tail.stdout.on("data", function (data) {
console.log(data);
});
Because neither tail nor spawn know how to expand file names with wild cards into the set of matching file names. That's normally performed by the shell, so in this case you'll need to do it yourself in code.

Resources