node.js fs.readdir recursive directory search

前端 未结 30 1570
醉酒成梦
醉酒成梦 2020-11-22 15:55

Any ideas on an async directory search using fs.readdir? I realise that we could introduce recursion and call the read directory function with the next directory to read, bu

30条回答
  •  情话喂你
    2020-11-22 16:22

    This is my answer. Hope it can help somebody.

    My focus is to make the searching routine can stop at anywhere, and for a file found, tells the relative depth to the original path.

    var _fs = require('fs');
    var _path = require('path');
    var _defer = process.nextTick;
    
    // next() will pop the first element from an array and return it, together with
    // the recursive depth and the container array of the element. i.e. If the first
    // element is an array, it'll be dug into recursively. But if the first element is
    // an empty array, it'll be simply popped and ignored.
    // e.g. If the original array is [1,[2],3], next() will return [1,0,[[2],3]], and
    // the array becomes [[2],3]. If the array is [[[],[1,2],3],4], next() will return
    // [1,2,[2]], and the array becomes [[[2],3],4].
    // There is an infinity loop `while(true) {...}`, because I optimized the code to
    // make it a non-recursive version.
    var next = function(c) {
        var a = c;
        var n = 0;
        while (true) {
            if (a.length == 0) return null;
            var x = a[0];
            if (x.constructor == Array) {
                if (x.length > 0) {
                    a = x;
                    ++n;
                } else {
                    a.shift();
                    a = c;
                    n = 0;
                }
            } else {
                a.shift();
                return [x, n, a];
            }
        }
    }
    
    // cb is the callback function, it have four arguments:
    //    1) an error object if any exception happens;
    //    2) a path name, may be a directory or a file;
    //    3) a flag, `true` means directory, and `false` means file;
    //    4) a zero-based number indicates the depth relative to the original path.
    // cb should return a state value to tell whether the searching routine should
    // continue: `true` means it should continue; `false` means it should stop here;
    // but for a directory, there is a third state `null`, means it should do not
    // dig into the directory and continue searching the next file.
    var ls = function(path, cb) {
        // use `_path.resolve()` to correctly handle '.' and '..'.
        var c = [ _path.resolve(path) ];
        var f = function() {
            var p = next(c);
            p && s(p);
        };
        var s = function(p) {
            _fs.stat(p[0], function(err, ss) {
                if (err) {
                    // use `_defer()` to turn a recursive call into a non-recursive call.
                    cb(err, p[0], null, p[1]) && _defer(f);
                } else if (ss.isDirectory()) {
                    var y = cb(null, p[0], true, p[1]);
                    if (y) r(p);
                    else if (y == null) _defer(f);
                } else {
                    cb(null, p[0], false, p[1]) && _defer(f);
                }
            });
        };
        var r = function(p) {
            _fs.readdir(p[0], function(err, files) {
                if (err) {
                    cb(err, p[0], true, p[1]) && _defer(f);
                } else {
                    // not use `Array.prototype.map()` because we can make each change on site.
                    for (var i = 0; i < files.length; i++) {
                        files[i] = _path.join(p[0], files[i]);
                    }
                    p[2].unshift(files);
                    _defer(f);
                }
            });
        }
        _defer(f);
    };
    
    var printfile = function(err, file, isdir, n) {
        if (err) {
            console.log('-->   ' + ('[' + n + '] ') + file + ': ' + err);
            return true;
        } else {
            console.log('... ' + ('[' + n + '] ') + (isdir ? 'D' : 'F') + ' ' + file);
            return true;
        }
    };
    
    var path = process.argv[2];
    ls(path, printfile);
    

提交回复
热议问题