Получить все каталоги в каталоге nodejs

Я надеялся, что это будет просто, но я не могу найти что-нибудь там, чтобы сделать это.

Я просто хочу получить все папки/каталоги в пределах данной папки/каталога.

Итак, например:

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

Я ожидаю получить массив из:

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

Или выше с дорожкой, если это было так, как это было подано...

Итак, что-то уже существует, чтобы сделать выше?

Ответ 1

Вот более короткая синхронная версия этого ответа, которая может перечислить все каталоги (скрытые или нет) в текущем каталоге:

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)

Ответ 2

Благодаря синтаксису 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
}

Ответ 3

Список каталогов с использованием пути.

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

Ответ 4

Рекурсивное решение

Я пришел сюда в поисках способа получить все подкаталоги, все их подкаталоги и т.д. Основываясь на принятом ответе, я написал следующее:

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))];
}

Ответ 5

Это должно сделать это:

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 (async)

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 (async)

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})); 
            }
        }
    });
}

Ответ 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

Отказ от ответственности: я автор.

Ответ 7

С Node.js версия> = v10.13.0, fs.readdirSync будет возвращать массив fs.Dirent объектов, если withFileTypes опция установлена true.

Так что вы можете использовать,

const fs = require('fs')

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

Ответ 8

И для асинхронной версии 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);
  });
}

Ответ 9

Использование fs-extra, которое обещает асинхронные вызовы fs, и новый синтаксис await async:

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("/")

Ответ 10

версия 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

Зависит от async

В качестве альтернативы используйте для этого модуль! (Есть модули для всего. [Править]

Ответ 11

Если вам нужна вся версия async. У вас может быть что-то вроде этого.

  • Запишите длину каталога, используя его как индикатор, чтобы узнать, завершены ли все задачи async stat.

  • Если задачи async stat завершены, проверяется весь файл stat, поэтому вызывайте обратный вызов

Это будет работать только до тех пор, пока Node.js является единственным потоком, поскольку он не предполагает, что никакие две задачи async не будут увеличивать счетчик одновременно.

'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, завернув задачу async в объект 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);
    });
});

Ответ 12

используйте fs, путь, модуль может получить папку. это использование Promise. Если вы получите заполнение, вы можете изменить isDirectory() на isFile() Nodejs-- fs - fs.Stats. Наконец, вы можете получить имя файла file'extname и т.д. 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);
    });
});

Ответ 13

Этот ответ не использует блокирующие функции, такие как readdirSync или statSync. Он не использует внешние зависимости и не оказывается в глубине ада обратного вызова.

Вместо этого мы используем современные удобства JavaScript, такие как Promises и async-await синтаксис. А асинхронные результаты обрабатываются параллельно; не последовательно -

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 использованный выше, представлял собой шаблон, извлеченный из набора функций, предназначенных для решения аналогичной проблемы. Для более подробного объяснения см. Связанные вопросы и ответы.

Ответ 14

 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));''

Ответ 15

На всякий случай, если кто-то попадает сюда из веб-поиска и имеет 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);
}

Ответ 16

Еще один рекурсивный подход

Спасибо 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)
    }
}, [])

Ответ 17

функциональное программирование

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)
    }
}

Ответ 18

Полностью асинхронная версия с 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);
}

Протестировано, работает хорошо.

Ответ 19

Я лично объявлял бы функцию stat из цикла - что-то вроде этого:

function getSubDirs(dir, cb) {
    fs.readdir(dir, function(err, files) {
        var dirs = [],
        filePath,
        checkDirectory = function(err, stat) {
            if(stat.isDirectory()) {
                dirs.push(files[i]);
            }
            if(i + 1 === l) { // last record
                cb(dirs);
            }
        };

        for(var i=0, l=files.length; i<l; i++) {
            if(files[i][0] !== '.') { // ignore hidden
                filePath = dir+'/'+files[i];
                fs.stat(filePath, checkDirectory);
            }
        }
    });
}

JShint жалуется на другое, и вы можете создавать новый вызов функции на каждом проходе цикла.

Ответ 20

Асинхронный/ожидающий вариант:

async function getFolders(path) {
    let result = Array();
    let files = await fs.readdir(path);
    for (let i = 0; i < files.length; i++) {
        var filePath = path + '/' + file;
        if (await fs.stat(filePath).isDirectory()) {
            result.push(filePath);
        }
    }

    return result;
}

Я также рекомендую использовать fs-extra вместо fs..