Node by example

Node by example: 5. Child Process & File System

5. Child Process & File System

The complete source code can be downloaded here: http://github.com/Hendrik/node-by-example

Child Process: [1]
Please note:
at the time of this writing the api has changed and ChildProcess is now its own class, so please make sure you have the latest node version installed ( >=0.1.33 required ).
What was previously createChildProcess() is now require("child_process").spawn().

Node provides a tri-directional popen(3) facility through the ChildProcess class. It is possible to stream data through the child’s stdin, stdout, and stderr in a fully non-blocking way.

To create a child process use require("child_process").spawn().

See 05_ChildProc_FS/childprocess.js:

var    sys = require("sys"),
  filename = process.ARGV[2];

if (!filename)
  return sys.puts("Usage: node " + __filename.replace(__dirname + "/", "") + " filename");

// create child process and add a listener for its "output" event
var tail = process.createChildProcess("tail", ["-f", filename]);
tail.addListener("output", function(data) {
  sys.puts(data);
});
tail.addListener("exit", function(code) {
  sys.puts("Child process stopped with exit code: " + code);
});

// add a timer to kill the child proces after 10 seconds
setTimeout(function() {
  tail.kill();
}, 10000);

 

This is a small node app, which will "tail -f" a provided file through a child process and send the output to the command prompt. A timeout event will kill the child process after 10 seconds.

First we create the child process:

var tail = process.createChildProcess("tail", ["-f", filename]);

Then we attach event listeners for the "output" and "exit" events.
The "output" event is emitted every time the child process sends data to its stdout.
In this case we simply output the data to the comand prompt:

tail.addListener("output", function(data) {
  sys.puts(data);
});

The output can be handled as needed, e.g.: you could modify this app to send the output through a websocket to a browser.

After 10 seconds, the setTimeout function will kill the child process:

setTimeout(function() {
  tail.kill();
}, 10000);

This will then emitt the "exit" event with the final exit code of the child process:

tail.addListener("exit", function(code) {
  sys.puts("Child process stopped with exit code: " + code);
});

 

The child_process module also provides a high-level way to execute a command as a child process and buffer the output and return it in a callback via:

require("child_process").exec(command, callback)

The callback gets the arguments (err, stdout, stderr). On success "err" will be "null". On error "err" will be an instance of "Error" and "err.code" will be the exit code of the child process.

 

See 05_ChildProc_FS/childprocess_2.js:

var sys = require("sys"),
   exec = require("child_process").exec;

exec("ls /", function (err, stdout, stderr) {
  if (err) throw err;
  sys.puts(stdout);
});

 

File System: [2]

File I/O is provided by simple wrappers around standard POSIX functions. To use this module do require('fs'). All the methods have asynchronous and synchronous forms. Keep in mind that with the asynchronous methods there is no guaranteed ordering!

The following example code will: 1. create a new file "test_file.txt" 2. write data to the file 3. add a file watcher to it to monitor any changes 3. change the chmod value of the file 4. file watcher triggers due to the change 5. output the content of the current directory 6. output the content of "test_file.txt" 7. stop the file watcher 8. delete the file

1. create a new file: fs.open() opens a file, in this case for writing.

var file = path.join(__dirname, "test_file.txt");

fs.open(file, "w", 0644, function(err, fd) {
  if (err) throw err;
});

 

2. write data to the file: fs.write() allow you to write data to the file. Once the data has been written the file gets closed using the synchronous fs.closeSync() method.

  fs.write(fd, "Hello World", 0, "utf8", function(err, written) {
    if (err) throw err;
    fs.closeSync(fd);
  });

 

3. add a file watcher to it to monitor any changes: fs.watchFile() will monitor the provided file and triggers its callback whenever the file has been changed.

    fs.watchFile(file, function(curr, prev) {
      sys.puts("\n\ttest_file.txt has been edited");
      sys.puts("\tThe current mtime is: " + curr.mtime);
      sys.puts("\tThe previous mtime was: " + prev.mtime + "\n");
    });

 

4. change the chmod value of the file: Use fs.chmod() to change the chmod value of the specified file.

  fs.chmod(file, 0777, function(err) {
    if (err) throw err;
  });

 

5. output the content of the current directory fs.readdir() reads the entire content of the provided directory in an array excluding '.' and '..'.

  fs.readdir(__dirname, function(err, files) {
    if (err) throw err;
    sys.puts(JSON.stringify(files));
    show_file_content();
  });

 

6. output the content of "test_file.txt" fs.readFile() reads the content of the provided file.

  fs.readFile(file, function(err, content) {
    if (err) throw err;
    sys.puts(content);
  });

 

7. stop the file watcher To stop a file watcher simply use unwatchFile().

  fs.unwatchFile(file);

 

8. delete the file To delete a file use fs.unlink().

  fs.unlink(file, function(err) {
    if (err) throw err;
  });

 

See 05_ChildProc_FS/filesystem.js:

var sys = require("sys"),
   path = require("path"),
     fs = require("fs");

var file = path.join(__dirname, "test_file.txt");

fs.open(file, "w", 0644, function(err, fd) {
  if (err) throw err;
  sys.puts("File test_file.txt opened");
  fs.write(fd, "Hello World", 0, "utf8", function(err, written) {
    sys.puts("Data written");
    if (err) throw err;
    fs.closeSync(fd);

    fs.watchFile(file, function(curr, prev) {
      sys.puts("\n\ttest_file.txt has been edited");
      sys.puts("\tThe current mtime is: " + curr.mtime);
      sys.puts("\tThe previous mtime was: " + prev.mtime + "\n");
    });

    chmod_file();
  });
});

function chmod_file() {
  fs.chmod(file, 0777, function(err) {
    if (err) throw err;
    sys.puts("\nchmod value of test_file.txt set to 777");

    fs.chmodSync(file, 0644);
    sys.puts("chmod value of test_file.txt set to 644");

    show_dir();
  });
}

function show_dir() {
  sys.puts("\nContent of " + __dirname + ":");
  fs.readdir(__dirname, function(err, files) {
    if (err) throw err;
    sys.puts(JSON.stringify(files));
    show_file_content();
  });
}

function show_file_content() {
  fs.readFile(file, function(err, content) {
    if (err) throw err;
    sys.puts("\nContent of test_file.txt:");
    sys.puts(content);
    delete_file();
  });
}

function delete_file() {
  fs.unwatchFile(file);
  sys.puts("\nStopping watchFile of test_file.txt");

  fs.unlink(file, function(err) {
    if (err) throw err;
    sys.puts("\ntest_file.txt has been deleted.");
  });
}

 

[1] http://nodejs.org/api.html#child-processes-79
[2] http://nodejs.org/api.html#file-system-87