How do you recursively get a list of all folder names in a directory in Node.js? [closed]

不想你离开。 提交于 2020-05-22 09:57:50

问题


I'm trying to get a list of all the folders present in a directory recursively. I then want to get a list of all the folders in each of those folders.

I want to continue to do this recursively. How can I do this?

const folder = 'folder1/';
const fs = require('fs');

fs.readdir(folder, (err, files) => {
  files.forEach(file => {
    // detect folder and continue to look for sub-folders
  });
});

回答1:


Here's an implementation that recursively finds all of the sub directories using Promises:

const fs = require('fs');
const path = require('path');

// Uncomment polyfill if using Node.js < 10.x
// const util = require('util');
// fs.promises = {
//     readdir: util.promisify(fs.readdir),
//     stat: util.promisify(fs.stat)
// };

async function listDirectories(dir) {
    const fileNames = await fs.promises.readdir(dir);

    // Create a directory listing
    const listings = await Promise.all(
        fileNames.map(async (name) => {
            const file = path.join(dir, name);
            const stat = await fs.promises.stat(file);
            return {file, stat};
        })
    );

    // Create a list of all the sub directories using the listings
    const subDirs = listings.filter((listing) => listing.stat.isDirectory())
        .map((listing) => listing.file);

    // Recurse over the sub directories and add their sub directories to the list of directories
    const subSubDirs = await Promise.all(subDirs.map(listDirectories));
    return subSubDirs.reduce((a, b) => a.concat(b), subDirs);
}

listDirectories('folder1/').then((dirs) => {
    console.log(dirs);
}).catch((err) => {
    process.exitCode = 1;
    console.error(err);
});

Compared to an equivalent callback based solution I think the promise based solution is easier to understand and less error prone:

const fs = require('fs');
const path = require('path');

function listDirectories(dir, callback) {
    fs.readdir(dir, (err, fileNames) => {
        if (err) return callback(err);
        if (!fileNames.length) return callback(null, []);

        // We have to keep track of the remaining operations
        let remaining = fileNames.length;

        const subDirs = [];
        fileNames.forEach((name) => {
            const file = path.join(dir, name);
            fs.stat(file, (err, stats) => {
                if (err) return callback(err);

                if (stats.isDirectory()) {
                    subDirs.push(file);
                    listDirectories(file, (err, subSubDirs) => {
                        if (err) return callback(err);
                        subDirs.push(...subSubDirs);
                        if (!--remaining) {
                            // We've gathered the sub dirs of this sub dir and this was the last file to check, all done.
                            callback(null, subDirs);
                        }
                    });
                } else if (!--remaining) {
                    // File was not a dir and was the last file to check, all done.
                    callback(null, subDirs);
                }
            });
        });
    });
}

listDirectories('folder1/',(err, dirs) => {
    if (err) {
        process.exitCode = 1;
        console.error(err);
    } else {
        console.log(dirs);
    }
});



回答2:


Level -99: Callback Hell

It's suffocating down here and I need to get some air. We can easily get to the surface by use of fs.promises and the newer async-await syntaxes -

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const dirs = async (path = ".") =>
  (await stat (path)) .isDirectory ()
    ? Promise
        .all
          ( (await readdir (path))
              .map (p => dirs (join (path, p)))
          )
        .then
          ( results =>
              [] .concat (path, ...results)
          )
    : []

dirs (process.argv[2]) .then (console.log, console.error)

In my terminal, I install a sample package and then test the program on our project's directory -

$ npm install ramda
$ node main.js .

[ '.'
, 'node_modules'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/es/internal'
, 'node_modules/ramda/src'
, 'node_modules/ramda/src/internal'
]

We could add a depth parameter which controls how deep dirs should recur -

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const dirs = async (path = ".", depth = Infinity) =>
  (await stat (path)) .isDirectory ()
    ? depth === -1
        ? []
        : Promise
            .all
              ( (await readdir (path))
                  .map (p => dirs (join (path, p), depth - 1))
              )
            .then
              ( results =>
                  [] .concat (path, ...results)
              )
    : []

dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)

Testing in the terminal again, we show various paths and depths -

$ node main.js . 1
[ '.'
, 'node_modules'
]


$ node main.js . 2
[ '.'
, 'node_modules'
, 'node_modules/ramda'
]

$ node main.js node_modules/ 1
[ 'node_modules/'
, 'node_modules/ramda'
]

$ node main.js node_modules/ 2
[ 'node_modules/'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/src'
]

Going Higher-Level

This question is related to another question I answered here. The answer there uses a variety of techniques to provide a complete solution but goes on to show certain patterns can be extracted and reused.

In the dirs solution above we only have one function to review so we cannot identify a pattern. But I'll show how the reusable module that emerged in the other answer, Parallel, can be applied to this dirs as well -

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const Parallel =
  require ('./parallel')

const dirs = async (path = ".", depth = Infinity) =>
  (await stat (path)) .isDirectory ()
    ? depth === -1
        ? []
        : Parallel (readdir (path))
            .flatMap (f => dirs (join (path, f), depth - 1))
            .then (results => [ path, ...results ])
    : []

dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)

Follow the link above to get an idea of how Parallel can be used to accomplish similar tasks




回答3:


You can use IsFile and IsDirectory

files.forEach(file => {
    fs.stat(file, function (error, stat) {
      if (stat.isFile()) // it's a file
      if (stat.isDirectory()) // it's a directory
    });
  });


来源:https://stackoverflow.com/questions/56084258/how-do-you-recursively-get-a-list-of-all-folder-names-in-a-directory-in-node-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!