获取目录nodejs中的所有目录


260

我希望这将是一件简单的事情,但是我找不到任何可以做到的事情。

我只想获取给定文件夹/目录中的所有文件夹/目录。

因此,例如:

<MyFolder>
|- SomeFolder
|- SomeOtherFolder
|- SomeFile.txt
|- SomeOtherFile.txt
|- x-directory

我希望得到一个数组:

["SomeFolder", "SomeOtherFolder", "x-directory"]

或者上面的路径,如果那是它的服务方式...

那么已经有什么可以做以上的事情吗?

Answers:


463

这是此答案的较短的同步版本,它可以列出当前目录中的所有目录(是否隐藏):

const { lstatSync, readdirSync } = require('fs')
const { join } = require('path')

const isDirectory = source => lstatSync(source).isDirectory()
const getDirectories = source =>
  readdirSync(source).map(name => join(source, name)).filter(isDirectory)

节点10.10.0+的更新

我们可以使用的新withFileTypes选项readdirSync来跳过额外的lstatSync通话:

const { readdirSync } = require('fs')

const getDirectories = source =>
  readdirSync(source, { withFileTypes: true })
    .filter(dirent => dirent.isDirectory())
    .map(dirent => dirent.name)

14
小心,您需要绝对路径来获取文件状态。require('path').resolve(__dirname, file)
Silom 2014年

9
@pilau它只是不适用于相对路径,这就是为什么您需要对其进行规范化。为此,您可以使用path.resolve。
Silom

1
@rickysullivan:您需要遍历响应,并对其中的每个文件夹使用path.resolve(srcpath,foldername)
jarodsmk

5
由于您使用的是es6:const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
pmrotule '17

2
请注意,如果目录中有符号链接,则此操作会中断。使用lstatSync代替。
戴夫

103

得益于JavaScript ES6(ES2015)语法功能,它成为了一种衬板:

同步版本

const { readdirSync, statSync } = require('fs')
const { join } = require('path')

const dirs = p => readdirSync(p).filter(f => statSync(join(p, f)).isDirectory())

Node.js 10+的异步版本(实验性)

const { readdir, stat } = require("fs").promises
const { join } = require("path")

const dirs = async path => {
  let dirs = []
  for (const file of await readdir(path)) {
    if ((await stat(join(path, file))).isDirectory()) {
      dirs = [...dirs, file]
    }
  }
  return dirs
}

30
将某事物强制到单行使其可读性降低,因此不那么需要它。
rlemon '16

7
您可以将任何一条指令放在一行中,并不意味着您应该这样做。
凯文B

4
已投票。不知道为什么人们对此表示反对。一根内衬不好但很常见,您可以根据需要对其进行美化。关键是您从这个答案中学到了一些东西
Aamir Afridi

27
已投票。尽管有理由批评它应该分布在更多行上。这个答案证明了新语法的语义优势,但是我认为人们对它被挤在一行上的方式越来越分心。如果将此代码散布在与接受的答案相同的行数上,则它仍然是同一机制的更简洁的表达。我认为该答案具有一定价值,但也许应该对已接受的答案进行修改,而不是单独的答案。
Iron Savior

23
你们是认真的吗?这是一个完美而有效的答案!一条抄袭izing窃-希望有一种方法可以降低糟糕的借口。这不是火箭科学。这是一个非常简单而愚蠢的操作!不要为了它而冗长!肯定会获得我的+1!
Mrchief

36

使用路径列出目录。

function getDirectories(path) {
  return fs.readdirSync(path).filter(function (file) {
    return fs.statSync(path+'/'+file).isDirectory();
  });
}

1
这很简单
Paula Fleck

最后我能理解的一个
FourCinnamon0

21

递归解

我来这里的目的是寻找一种获取所有子目录及其所有子目录等的方法。在接受的答案的基础上,我这样写:

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

function flatten(lists) {
  return lists.reduce((a, b) => a.concat(b), []);
}

function getDirectories(srcpath) {
  return fs.readdirSync(srcpath)
    .map(file => path.join(srcpath, file))
    .filter(path => fs.statSync(path).isDirectory());
}

function getDirectoriesRecursive(srcpath) {
  return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))];
}

这正是我在寻找的东西,它似乎工作得很好,除了第一个路径以外的每个路径都像这样:“ src \\ pages \\ partials \\ buttons”,而不是“ src / pages / partials / buttons” 。我添加了这个肮脏的修复程序:var res = getDirectoriesRecursive(srcpath); res = res.map(function(x){return x.replace(/ \\ / g,“ /”)}); console.log(res);
PaulB

1
较不脏的方法是path.normalize()。nodejs.org/api/path.html#path_path_normalize_path
Patrick McElhaney

您将返回父目录,这是不希望的。我将重构getDirectoriesRecursive以防止出现这种情况: if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; }
Nadav

10

应该这样做:

CoffeeScript(同步)

fs = require 'fs'

getDirs = (rootDir) ->
    files = fs.readdirSync(rootDir)
    dirs = []

    for file in files
        if file[0] != '.'
            filePath = "#{rootDir}/#{file}"
            stat = fs.statSync(filePath)

            if stat.isDirectory()
                dirs.push(file)

    return dirs

CoffeeScript(异步)

fs = require 'fs'

getDirs = (rootDir, cb) ->
    fs.readdir rootDir, (err, files) ->
        dirs = []

        for file, index in files
            if file[0] != '.'
                filePath = "#{rootDir}/#{file}"
                fs.stat filePath, (err, stat) ->
                    if stat.isDirectory()
                        dirs.push(file)
                    if files.length == (index + 1)
                        cb(dirs)

JavaScript(异步)

var fs = require('fs');
var getDirs = function(rootDir, cb) { 
    fs.readdir(rootDir, function(err, files) { 
        var dirs = []; 
        for (var index = 0; index < files.length; ++index) { 
            var file = files[index]; 
            if (file[0] !== '.') { 
                var filePath = rootDir + '/' + file; 
                fs.stat(filePath, function(err, stat) {
                    if (stat.isDirectory()) { 
                        dirs.push(this.file); 
                    } 
                    if (files.length === (this.index + 1)) { 
                        return cb(dirs); 
                    } 
                }.bind({index: index, file: file})); 
            }
        }
    });
}

1
尽管如果这是用于生产系统,则您确实要避免使用同步fs方法。
亚伦·迪富

20
请注意阅读此答案的所有新手:这是CoffeeScript,而不是JavaScript(我的朋友很困惑地告诉我,为什么JavaScript突然具有语义空白)。
DallonF

1
@nicksweet可以将其转换为JS吗?
mikemaccana 2015年

1
这个答案有一些明显的问题:它没有任何错误处理;(回调签名应为(err, dirs));在存在点文件或文件夹的情况下,它不会回叫;它容易受到所有比赛条件的影响;它可能会在检查所有条目之前回叫。
1j01 2015年

21
例如,人们需要停止破坏同步API。确定是否应使用同步版本不是由“生产”决定的。同样重复广告,异步API更好,同步不好,没有上下文,只是不准确。我希望JS社区不要再发布此消息。同步更简单(是),但会阻止消息循环(boo)。因此,不要在不想阻止的服务器上使用sync api,而可以在构建脚本中随意使用它们,例如,在没有关系的地方。</ rant>
hcoverlambda '16

6

另外,如果您可以使用外部库,则可以使用filehound。它支持回调,承诺和同步调用。

使用承诺:

const Filehound = require('filehound');

Filehound.create()
  .path("MyFolder")
  .directory() // only search for directories
  .find()
  .then((subdirectories) => {
    console.log(subdirectories);
  });

使用回调:

const Filehound = require('filehound');

Filehound.create()
  .path("MyFolder")
  .directory()
  .find((err, subdirectories) => {
    if (err) return console.error(err);

    console.log(subdirectories);
  });

同步通话:

const Filehound = require('filehound');

const subdirectories = Filehound.create()
  .path("MyFolder")
  .directory()
  .findSync();

console.log(subdirectories);

有关更多信息(和示例),请查看文档:https : //github.com/nspragg/filehound

免责声明:我是作者。


5

在node.js版本> = v10.13.0的情况下,如果将option设置为,则fs.readdirSync将返回fs.Dirent对象的数组。withFileTypestrue

所以你可以使用

const fs = require('fs')

const directories = source => fs.readdirSync(source, {
   withFileTypes: true
}).reduce((a, c) => {
   c.isDirectory() && a.push(c.name)
   return a
}, [])

好点,但是.filter(c => c.isDirectory())比使用它更简单reduce()
Emmanuel Touzery 18/12/30

是的,但是filter返回一个fs.Dirent对象数组,它们是目录。OP需要目录名称。
Mayur

1
是的,尽管如此,我还是会.filter(c => c.isDirectory()).map(c => c.name)reduce电话更喜欢。
伊曼纽尔·图扎里

我明白你的意思。我想读者可以根据自己的用例做出决定。我想说,与从磁盘读取的I / O相比,即使从SSD读取,在内存阵列上循环的开销也可以忽略不计,但是像往常一样,如果人们真的在乎,它们可以测量。
伊曼纽尔·图扎里

5
 var getDirectories = (rootdir , cb) => {
    fs.readdir(rootdir, (err, files) => {
        if(err) throw err ;
        var dirs = files.map(filename => path.join(rootdir,filename)).filter( pathname => fs.statSync(pathname).isDirectory());
        return cb(dirs);
    })

 }
 getDirectories( myDirectories => console.log(myDirectories));``

4

使用fs-extra(它允许异步fs调用)和新的await异步语法:

const fs = require("fs-extra");

async function getDirectories(path){
    let filesAndDirectories = await fs.readdir(path);

    let directories = [];
    await Promise.all(
        filesAndDirectories.map(name =>{
            return fs.stat(path + name)
            .then(stat =>{
                if(stat.isDirectory()) directories.push(name)
            })
        })
    );
    return directories;
}

let directories = await getDirectories("/")

3

对于getDirectories的异步版本,您需要以下异步模块

var fs = require('fs');
var path = require('path');
var async = require('async'); // https://github.com/caolan/async

// Original function
function getDirsSync(srcpath) {
  return fs.readdirSync(srcpath).filter(function(file) {
    return fs.statSync(path.join(srcpath, file)).isDirectory();
  });
}

function getDirs(srcpath, cb) {
  fs.readdir(srcpath, function (err, files) {
    if(err) { 
      console.error(err);
      return cb([]);
    }
    var iterator = function (file, cb)  {
      fs.stat(path.join(srcpath, file), function (err, stats) {
        if(err) { 
          console.error(err);
          return cb(false);
        }
        cb(stats.isDirectory());
      })
    }
    async.filter(files, iterator, cb);
  });
}

2

此答案未使用诸如readdirSync或的阻止功能statSync。它不使用外部依赖关系,也不在回调地狱的深度中找到自己。

相反,我们使用诸如Promises和and async-await语法之类的现代JavaScript便利。并异步处理结果;不按顺序-

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)
          )
    : []

我将安装示例程序包,然后测试我们的功能-

$ npm install ramda
$ node

让我们看看它的工作原理-

> dirs (".") .then (console.log, console.error)

[ '.'
, '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'
]

使用通用模块Parallel,我们可以简化的定义dirs-

const Parallel =
  require ("./Parallel")

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

Parallel上面使用的模块是从一组旨在解决类似问题的功能中提取的模式。有关更多说明,请参见此相关问答


1

此答案的 CoffeeScript版本,带有适当的错误处理:

fs = require "fs"
{join} = require "path"
async = require "async"

get_subdirs = (root, callback)->
    fs.readdir root, (err, files)->
        return callback err if err
        subdirs = []
        async.each files,
            (file, callback)->
                fs.stat join(root, file), (err, stats)->
                    return callback err if err
                    subdirs.push file if stats.isDirectory()
                    callback null
            (err)->
                return callback err if err
                callback null, subdirs

取决于异步

或者,为此使用模块! (所有内容都有模块。[需要引用])


1

如果需要使用所有async版本。你可以有这样的东西。

  1. 记录目录长度,用作指示所有异步统计任务是否完成的指示符。

  2. 如果异步统计信息任务已完成,则所有文件统计信息均已检查完毕,因此请调用回调

仅当Node.js是单线程时,此方法才有效,因为它假定没有两个异步任务会同时增加计数器。

'use strict';

var fs = require("fs");
var path = require("path");
var basePath = "./";

function result_callback(results) {
    results.forEach((obj) => {
        console.log("isFile: " + obj.fileName);
        console.log("fileName: " + obj.isFile);
    });
};

fs.readdir(basePath, (err, files) => {
    var results = [];
    var total = files.length;
    var finished = 0;

    files.forEach((fileName) => {
        // console.log(fileName);
        var fullPath = path.join(basePath, fileName);

        fs.stat(fullPath, (err, stat) => {
            // this will work because Node.js is single thread
            // therefore, the counter will not increment at the same time by two callback
            finished++;

            if (stat.isFile()) {
                results.push({
                    fileName: fileName,
                    isFile: stat.isFile()
                });
            }

            if (finished == total) {
                result_callback(results);
            }
        });
    });
});

如您所见,这是一种“深度优先”的方法,这可能会导致回调地狱,而它并不是“功能性的”。人们尝试通过将异步任务包装到Promise对象中来解决Promise问题。

'use strict';

var fs = require("fs");
var path = require("path");
var basePath = "./";

function result_callback(results) {
    results.forEach((obj) => {
        console.log("isFile: " + obj.fileName);
        console.log("fileName: " + obj.isFile);
    });
};

fs.readdir(basePath, (err, files) => {
    var results = [];
    var total = files.length;
    var finished = 0;

    var promises = files.map((fileName) => {
        // console.log(fileName);
        var fullPath = path.join(basePath, fileName);

        return new Promise((resolve, reject) => {
            // try to replace fullPath wil "aaa", it will reject
            fs.stat(fullPath, (err, stat) => {
                if (err) {
                    reject(err);
                    return;
                }

                var obj = {
                    fileName: fileName,
                    isFile: stat.isFile()
                };

                resolve(obj);
            });
        });
    });

    Promise.all(promises).then((values) => {
        console.log("All the promise resolved");
        console.log(values);
        console.log("Filter out folder: ");
        values
            .filter((obj) => obj.isFile)
            .forEach((obj) => {
                console.log(obj.fileName);
            });
    }, (reason) => {
        console.log("Not all the promise resolved");
        console.log(reason);
    });
});

好代码!但是我认为它应该是Promise.all块中的“过滤出文件:”,因为它正在检查它是否是一个文件并记录下来。:)
比卡勒尼泊尔

1

使用fs,path模块可以得到该文件夹​​。这个使用Promise。如果您将得到填充,则可以将isDirectory()更改为isFile() Nodejs--fs--fs.Stats。最后,您可以获取文件'name file'的扩展名,以此类推,例如Nodejs--Path

var fs = require("fs"),
path = require("path");
//your <MyFolder> path
var p = "MyFolder"
fs.readdir(p, function (err, files) {
    if (err) {
        throw err;
    }
    //this can get all folder and file under  <MyFolder>
    files.map(function (file) {
        //return file or folder path, such as **MyFolder/SomeFile.txt**
        return path.join(p, file);
    }).filter(function (file) {
        //use sync judge method. The file will add next files array if the file is directory, or not. 
        return fs.statSync(file).isDirectory();
    }).forEach(function (files) {
        //The files is array, so each. files is the folder name. can handle the folder.
        console.log("%s", files);
    });
});

在审核队列中:我可以要求您在答案周围添加更多背景信息。仅代码的答案很难理解。如果您可以在帖子中添加更多信息,它将对询问者和未来的读者都有帮助。
help-info.de

1

带有ES6的完全异步版本,仅本地软件包fs.promises和async / await并行执行文件操作:

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

async function listDirectories(rootPath) {
    const fileNames = await fs.promises.readdir(rootPath);
    const filePaths = fileNames.map(fileName => path.join(rootPath, fileName));
    const filePathsAndIsDirectoryFlagsPromises = filePaths.map(async filePath => ({path: filePath, isDirectory: (await fs.promises.stat(filePath)).isDirectory()}))
    const filePathsAndIsDirectoryFlags = await Promise.all(filePathsAndIsDirectoryFlagsPromises);
    return filePathsAndIsDirectoryFlags.filter(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.isDirectory)
        .map(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.path);
}

经过测试,效果很好。


0

以防万一其他人最终通过网络搜索来到这里,并且在他们的依存关系列表中已经有了Grunt,对于这个问题的答案变得微不足道了。这是我的解决方案:

/**
 * Return all the subfolders of this path
 * @param {String} parentFolderPath - valid folder path
 * @param {String} glob ['/*'] - optional glob so you can do recursive if you want
 * @returns {String[]} subfolder paths
 */
getSubfolders = (parentFolderPath, glob = '/*') => {
    return grunt.file.expand({filter: 'isDirectory'}, parentFolderPath + glob);
}

0

另一种递归方法

感谢Mayur认识我withFileTypes。我编写了以下代码来递归获取特定文件夹的文件。可以轻松修改它以仅获取目录。

const getFiles = (dir, base = '') => readdirSync(dir, {withFileTypes: true}).reduce((files, file) => {
    const filePath = path.join(dir, file.name)
    const relativePath = path.join(base, file.name)
    if(file.isDirectory()) {
        return files.concat(getFiles(filePath, relativePath))
    } else if(file.isFile()) {
        file.__fullPath = filePath
        file.__relateivePath = relativePath
        return files.concat(file)
    }
}, [])

0

功能编程

const fs = require('fs')
const path = require('path')
const R = require('ramda')

const getDirectories = pathName => {
    const isDirectory = pathName => fs.lstatSync(pathName).isDirectory()
    const mapDirectories = pathName => R.map(name => path.join(pathName, name), fs.readdirSync(pathName))
    const filterDirectories = listPaths => R.filter(isDirectory, listPaths)

    return {
        paths:R.pipe(mapDirectories)(pathName),
        pathsFiltered: R.pipe(mapDirectories, filterDirectories)(pathName)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.