0

I have some code that loops through a directory and retrieves the file name of each file. The code then retrieves the contents of each file (usually a number or a short text).

var config = {};
config.liveProcValues = {};


var path = require('path');
walk = function(dir, done) {
var results = {};
fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var pending = list.length;
    if (!pending) return done(null, results);
    list.forEach(function(file) {
        file = path.resolve(dir, file);
        fs.stat(file, function(err, stat) {
            if (stat && stat.isDirectory()) {
                walk(file, function(err, res) {
                    results = results.concat(res);
                    if (!--pending) done(null, results);
                });
            } else {


                fs.readFileSync(file, 'utf8', function(err, data) {

                    if (err) {
                        contents = err;
                    } else {
                        contents = data;
                    }

                        console.log(filename + " - " + contents);


                    filename = file.replace(/^.*[\\\/]/, '');
                    config.liveProcValues[filename] = contents;


                });

The console.log line successfully outputs the right information, however when trying to store it into JSON:

  config.liveProcValues[filename] = contents;

It simply does remember the information.

walk("testdirectory", function(err, results) {
   if (err) throw err;
});

// Output the configuration
console.log(JSON.stringify(config, null, 2));
Corbin
  • 414
  • 5
  • 19
  • What do your filenames look like? Are you sure the pattern in `replace` doesn't result in an empty string? – marekful Jul 07 '15 at 12:51
  • When i run console.log on the filename it returns the correct value – Corbin Jul 07 '15 at 12:52
  • @Felix King - why duplicate? – Corbin Jul 07 '15 at 12:53
  • The file names are test1.txt and test2.txt – Corbin Jul 07 '15 at 12:54
  • Because it explains the issue you are experiencing. – Felix Kling Jul 07 '15 at 12:55
  • What explains the issue I am experiencing? – Corbin Jul 07 '15 at 12:56
  • 1
    The duplicate. In your case the solution is rather simple: Move the `console.log` call inside the `walk` callback. – Felix Kling Jul 07 '15 at 12:57
  • the console.log works fine.... its the config.liveProcValues[filename] = contents that doesn't work Simply moving that into the walk call does not work – Corbin Jul 07 '15 at 13:02
  • 1
    `config.liveProcValues[filename] = contents;` works just fine, but you are trying to log the object **before** `fs.readdir` is done. Another issue is that you are calling `results = results.concat(res);` but `results` is an object, so this will throw an error at some point. Yet another issue seems to be that you are not calling `done` if there are no sub-directories but only files. – Felix Kling Jul 07 '15 at 13:05
  • Thanks - starting to understand now - how would I log it in a way that works? – Corbin Jul 07 '15 at 13:10
  • The callback is supposed to be called when the file system was traversed. Anything that needs access to the result of the walk has to be inside or called from the callback. Then you just have to make sure that the callback really is called after you are done traversing (which currently is not the case). Look at the branches where you are calling `done`: Either if there are no files in the directory or if it contains a subdirectory. – Felix Kling Jul 07 '15 at 13:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82604/discussion-between-corbin-and-felix-kling). – Corbin Jul 07 '15 at 13:16
  • Wrote an answer instead. – Felix Kling Jul 07 '15 at 13:30

1 Answers1

0

You have to make sure that you are accessing the data after the filesystem was traversed. In order to do that you have to move the console.log into the walk callback:

walk("testdirectory", function(err, results) {
  if (err) throw err;

  // Output the configuration
  console.log(JSON.stringify(results, null, 2));
});

See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference for more info.

That alone won't solve the issue though, since you have a couple of logic errors in your code. You are trying to treat an object as an array (results.concat) and you are not always calling done when you are done (in particular, you are not calling done after you finished reading the files in a directory).


Here is a version that should come closer do what you want.

This uses Object.assign to merge two objects, which is not available in Node yet, but you can find modules that provide the same functionality.

Note thats I also removed the whole config object. It's cleaner if you work with results.

var path = require('path');
function walk(dir, done) {
  var results = {};
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    if (!list.length) return done(null, results);

    var pending = list.length;
    list.forEach(function(file) {
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            if (!err) {
              // Merge recursive results
              Object.assign(results, res);
            }
            if (!--pending) done(null, results);
          });
        } else {
          fs.readFile(file, 'utf8', function(err, data) {
            var contents = err || data;
            console.log(file + " - " + contents);
            file = file.replace(/^.*[\\\/]/, '');
            // Assign the result to `results` instead of a shared variable
            results[file] = contents;
            // Need to call `done` if there are no more files to read
            if (!--pending) done(null, results);
          });
        }
      });
    });
  });
}

But instead of writing your own walk implementation, you could also use an existing package.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143