Récupère tous les répertoires du répertoire nodejs

260

J'espérais que ce serait une chose simple, mais je ne trouve rien pour le faire.

Je veux juste obtenir tous les dossiers / répertoires dans un dossier / répertoire donné.

Ainsi, par exemple:

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

Je m'attendrais à obtenir un tableau de:

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

Ou ce qui précède avec le chemin si c'était comme ça qu'il était servi ...

Existe-t-il déjà quelque chose pour faire ce qui précède?

Grofit
la source

Réponses:

463

Voici une version plus courte et synchrone de cette réponse qui peut répertorier tous les répertoires (masqués ou non) du répertoire actuel:

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)

Mise à jour pour Node 10.10.0+

Nous pouvons utiliser la nouvelle withFileTypesoption de readdirSyncpour ignorer l' lstatSyncappel supplémentaire :

const { readdirSync } = require('fs')

const getDirectories = source =>
  readdirSync(source, { withFileTypes: true })
    .filter(dirent => dirent.isDirectory())
    .map(dirent => dirent.name)
Nick McCurdy
la source
14
Attention, vous avez besoin du chemin absolu pour obtenir la statistique du fichier. require('path').resolve(__dirname, file)
Silom
9
@pilau, cela ne fonctionne tout simplement pas avec un chemin relatif, c'est pourquoi vous devez le normaliser. Pour cela, vous pouvez utiliser path.resolve.
Silom
1
@rickysullivan: Vous devez parcourir la réponse et utiliser path.resolve (srcpath, foldername) pour chaque dossier à l'intérieur
jarodsmk
5
Puisque vous utilisez es6:const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
pmrotule
2
Notez que cela se casse s'il y a des liens symboliques dans le répertoire. Utilisez lstatSyncplutôt.
Dave
103

Grâce aux fonctionnalités de syntaxe JavaScript ES6 (ES2015), il s'agit d'un seul revêtement:

Version synchrone

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

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

Version asynchrone pour Node.js 10+ (expérimental)

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
}
pravdomil
la source
30
forcer quelque chose à une seule ligne le rend moins lisible et donc moins désiré.
rlemon
7
Vous pouvez adapter n'importe quelle instruction sur une seule ligne, cela ne signifie pas que vous le devriez.
Kevin B
4
A voté. Je ne sais pas pourquoi les gens ont voté pour cela. Un liner est mauvais mais commun, vous pouvez l'embellir à votre guise. Le fait est que vous avez appris quelque chose de cette réponse
Aamir Afridi
27
A voté. Bien qu'il soit une critique valable qu'il devrait être réparti sur plusieurs lignes. Cette réponse démontre l'avantage sémantique de la nouvelle syntaxe, mais je pense que les gens sont distraits par la façon dont elle a été entassée sur une seule ligne. Si vous répartissez ce code sur le même nombre de lignes que la réponse acceptée, il s'agit toujours d'une expression plus concise du même mécanisme. Je pense que cette réponse a une certaine valeur, mais elle mérite peut-être d'être une révision de la réponse acceptée plutôt qu'une réponse distincte.
Iron Savior
23
Êtes-vous sérieux? C'est une réponse parfaitement fine et valable! Une ligne, plagiat - souhaite qu'il y ait un moyen de rétrograder les excuses minables. Ce n'est pas sorcier. C'est une opération très simple et stupide! Ne soyez pas bavard pour le plaisir! Obtient mon +1 à coup sûr!
Mrchief
36

Répertorie les répertoires à l'aide d'un chemin.

function getDirectories(path) {
  return fs.readdirSync(path).filter(function (file) {
    return fs.statSync(path+'/'+file).isDirectory();
  });
}
Titlacauan
la source
1
Celui-ci est assez simple
Paula Fleck
Enfin un que je peux comprendre
FourCinnamon0
21

Solution récursive

Je suis venu ici à la recherche d'un moyen d'obtenir tous les sous-répertoires et tous leurs sous-répertoires, etc. En s'appuyant sur la réponse acceptée , j'ai écrit ceci:

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))];
}
Patrick McElhaney
la source
C'est exactement ce que je cherchais et cela semble très bien fonctionner sauf que chaque chemin sauf le premier apparaît comme ceci: "src \\ pages \\ partials \\ buttons" au lieu de "src / pages / partials / buttons" . J'ai ajouté ce correctif: var res = getDirectoriesRecursive (srcpath); res = res.map (fonction (x) {return x.replace (/ \\ / g, "/")}); console.log (res);
PaulB
1
Une façon moins sale de le faire est path.normalize (). nodejs.org/api/path.html#path_path_normalize_path
Patrick McElhaney
Vous renvoyez le répertoire parent, ce qui n'est pas souhaitable. Je voudrais refactoriser getDirectoriesRecursive pour éviter cela: if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; }
Nadav
10

Cela devrait le faire:

CoffeeScript (synchronisation)

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})); 
            }
        }
    });
}
nicksweet
la source
1
Bien que ce soit pour un système de production, vous voulez vraiment éviter les fsméthodes synchrones .
Aaron Dufour
20
Note à tous les débutants lisant cette réponse: c'est CoffeeScript, pas JavaScript (mon ami m'a envoyé un message de confusion en demandant pourquoi JavaScript avait soudainement un espace sémantique).
DallonF
1
@nicksweet Pouvez-vous le convertir en JS?
mikemaccana
1
Il y a quelques problèmes flagrants avec cette réponse: elle n'a pas de gestion d'erreur; (la signature de rappel doit être (err, dirs)); il ne rappellera pas en présence de fichiers ou dossiers dot; il est sensible à toutes les conditions de course; il peut rappeler avant d'avoir vérifié toutes les entrées.
1j01
21
Ug, les gens doivent arrêter de vilipender l'api de synchronisation. Déterminer si une version de synchronisation doit être utilisée n'est pas déterminé par le fait qu'il s'agit de "production". Répéter aussi à répétition que les API asynchrones sont meilleures et que la synchronisation est mauvaise, sans contexte, n'est tout simplement pas précis. Je souhaite que la communauté JS cesse de promulguer cela. La synchronisation est plus simple (yay) mais bloquera la boucle de message (boo). Donc, n'utilisez pas les API de synchronisation sur un serveur où vous ne voulez pas bloquer, mais n'hésitez pas à les utiliser dans un script de construction, par exemple là où cela n'a pas d'importance. </rant>
hcoverlambda
6

Alternativement, si vous pouvez utiliser des bibliothèques externes, vous pouvez utiliser filehound. Il prend en charge les rappels, les promesses et les appels de synchronisation.

Utiliser des promesses:

const Filehound = require('filehound');

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

Utilisation des rappels:

const Filehound = require('filehound');

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

    console.log(subdirectories);
  });

Appel de synchronisation:

const Filehound = require('filehound');

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

console.log(subdirectories);

Pour plus d'informations (et des exemples), consultez les documents: https://github.com/nspragg/filehound

Avertissement: je suis l'auteur.

nickool
la source
5

Avec la version node.js> = v10.13.0, fs.readdirSync renverra un tableau d' objets fs.Dirent si l' withFileTypesoption est définie sur true.

Vous pouvez donc utiliser,

const fs = require('fs')

const directories = source => fs.readdirSync(source, {
   withFileTypes: true
}).reduce((a, c) => {
   c.isDirectory() && a.push(c.name)
   return a
}, [])
Mayur
la source
bon point, mais .filter(c => c.isDirectory())serait plus simple que d'utiliserreduce()
Emmanuel Touzery
Oui, mais le filtre renvoie un tableau d'objets fs.Dirent qui sont des répertoires. L'OP voulait des noms de répertoires.
Mayur
1
c'est vrai, je préférerais quand même .filter(c => c.isDirectory()).map(c => c.name)l' reduceappel.
Emmanuel Touzery
Je vois ce que tu veux dire. Je suppose que les lecteurs SO peuvent décider en fonction de leur cas d'utilisation. Je dirais que le bouclage sur un tableau en mémoire devrait être négligeable en surcharge par rapport aux E / S de lecture à partir du disque, même si vous lisez à partir d'un disque SSD, mais comme d'habitude si on s'en soucie vraiment, ils peuvent mesurer.
Emmanuel Touzery
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));``
Jonathan bonzali
la source
4

Utilisation de fs-extra, qui promet les appels asynchrones fs, et la nouvelle syntaxe async en attente:

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("/")
1mike12
la source
3

Et une version asynchrone de getDirectories, vous avez besoin du module asynchrone pour cela:

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);
  });
}
JumpLink
la source
2

Cette réponse n'utilise pas de fonctions de blocage comme readdirSyncou statSync. Il n'utilise pas de dépendances externes et ne se retrouve pas dans les profondeurs de l'enfer de rappel.

Au lieu de cela, nous utilisons des fonctionnalités JavaScript modernes comme les promesses et les async-awaitsyntaxes. Et les résultats asynchrones sont traités en parallèle; pas séquentiellement -

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

Je vais installer un exemple de package, puis tester notre fonction -

$ npm install ramda
$ node

Voyons voir ça marche -

> 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'
]

En utilisant un module généralisé Parallel, nous pouvons simplifier la définition de 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 ])
    : []

Le Parallelmodule utilisé ci-dessus était un modèle extrait d'un ensemble de fonctions conçues pour résoudre un problème similaire. Pour plus d'explications, consultez ces questions et réponses connexes .

Je vous remercie
la source
1

Version CoffeeScript de cette réponse , avec une gestion correcte des erreurs:

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

Dépend de l' async

Sinon, utilisez un module pour cela! (Il y a des modules pour tout. [Citation nécessaire])

1j01
la source
1

Si vous devez utiliser toutes les asyncversions. Vous pouvez avoir quelque chose comme ça.

  1. Enregistrez la longueur du répertoire, utilisez-la comme indicateur pour savoir si toutes les tâches de statistiques asynchrones sont terminées.

  2. Si les tâches de statistiques asynchrones sont terminées, toutes les statistiques du fichier ont été vérifiées, appelez donc le rappel

Cela ne fonctionnera que tant que Node.js est un thread unique, car il suppose qu'aucune tâche asynchrone n'augmentera le compteur en même temps.

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

Comme vous pouvez le voir, il s'agit d'une approche "en profondeur d'abord", et cela pourrait entraîner un enfer de rappel, et ce n'est pas tout à fait "fonctionnel". Les gens essaient de résoudre ce problème avec Promise, en encapsulant la tâche asynchrone dans un objet 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);
    });
});
code4j
la source
Bon code! Mais je pense que ce devrait être "Filtrer les fichiers:" dans le bloc Promise.all car il vérifie s'il s'agit d'un fichier et l'enregistre. :)
Bikal Nepal
1

utiliser le module de chemin fs can peut obtenir le dossier. cette promesse d'utilisation. Si vous obtiendrez le remplissage, vous pouvez remplacer isDirectory () par isFile () Nodejs - fs - fs.Stats .Enfin, vous pouvez obtenir le fichier'name file'extname et ainsi de suite 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);
    });
});
Howard
la source
De la file d'attente d'examen: Puis-je vous demander de bien vouloir ajouter un peu plus de contexte autour de votre réponse. Les réponses uniquement codées sont difficiles à comprendre. Cela aidera le demandeur et les futurs lecteurs à la fois si vous pouvez ajouter plus d'informations dans votre message.
help-info.de
1

Version entièrement asynchrone avec ES6, seuls les packages natifs, fs.promises et async / attendent, effectuent les opérations de fichiers en parallèle:

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

Testé, cela fonctionne bien.

Dávid Veszelovszki
la source
0

Juste au cas où quelqu'un d'autre se retrouverait ici à partir d'une recherche sur le Web et aurait déjà Grunt dans sa liste de dépendances, la réponse à cela deviendrait triviale. Voici ma solution:

/**
 * 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);
}
Artif3x
la source
0

Une autre approche récursive

Merci à Mayur de m'avoir connu withFileTypes. J'ai écrit le code suivant pour obtenir les fichiers d'un dossier particulier récursivement. Il peut être facilement modifié pour n'obtenir que des répertoires.

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)
    }
}, [])
Cheikh Abdul Wahid
la source
0

programmation fonctionnelle

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)
    }
}
Roy Alcala
la source