Reputation: 3140
I'm trying to write a file templating script using Node.js. I have a JSON file called template.json
which stores template information. The idea behind my script is that, if I type something like:
tmpl.js java Joe
it will do the following:
touch Joe.java
template.json
to get the template for Java filesJoe
Joe.java
emacsclient Joe.java
Now, I wrote this script as follows:
#!/usr/local/bin/node --harmony
var templates = require('./config/template.json'),
args = process.argv;
if (args.length < 4) {
console.log("Not enough arguments!");
}
else {
var type = args[2],
name = args[3];
if (type in templates) {
var tmpl = templates[type],
contents = make_output(tmpl["format"],name),
file_name = name + tmpl["extension"],
command = "touch " + file_name + " && echo -e '" + contents +
"' &> " + file_name + " && emacsclient " + file_name;
invoke(command);
}
else {
console.log("No template for %s", type);
}
}
//Helpers
//Invokes comm with args in the terminal, returns all output
//Does not play nice with command redirection
function invoke(comm) {
var exec = require('child_process').exec,
child = exec(comm,
function (error, stdout, stderr) {
if (error !== null) {
console.log(stderr);
}
});
}
//If template is a format string, processes it with x as the
//replacement. Otherwise, just evaluates.
//Limited to one replacement at most.
function make_output(template, x) {
if(/.*\%s.*/i.test(template)) {
var util = require('util');
return util.format(template,x);
}
else {
return template;
}
}
Basically, the command it ends up building is something like:
touch Joe.java && echo -e `bunch of template stuffs` &> Joe.java && emacsclient Joe.java
Now, the problem I am getting is that the above command relies on output redirection, which my invoke
command doesn't deal with very well - specifically, everything executes, but I get an empty file! Is there a way I can change either invoke
or what I'm constructing to be invoked to avoid this problem?
Upvotes: 1
Views: 1825
Reputation: 151511
The issue is that Node's child_process.exec
starts sh
but you are using features that are peculiar to bash
. The &>
is interpreted as & >
in sh
(two operators: a control operator and a redirection operator) and echo -e
will use sh
's builtin implementation of echo
, which does not understand -e
.
It would probably be possible to work around the issues above but using the shell like you do is fragile. For instance if your template contains single quotes ('
), these quotes may interfere with the single quotes you use in your command. A more robust way to do it would be to change the main part of your code to use fs.writeFileSync
rather than using shell commands to write to your file:
var templates = require('./config/template.json'),
fs = require("fs"),
args = process.argv;
if (args.length < 4) {
console.log("Not enough arguments!");
}
else {
var type = args[2],
name = args[3];
if (type in templates) {
var tmpl = templates[type],
contents = make_output(tmpl["format"],name),
file_name = name + tmpl["extension"],
command = "emacsclient " + file_name;
fs.writeFileSync(file_name, contents);
invoke(command);
}
else {
console.log("No template for %s", type);
}
}
You'd also want to modify make_output
to perform the transformations that echo -e
would have done for you.
Upvotes: 3