Ruft alle Verzeichnisse im Verzeichnis nodejs ab


260

Ich hatte gehofft, dass dies eine einfache Sache sein würde, aber ich kann dort nichts herausfinden, um dies zu tun.

Ich möchte nur alle Ordner / Verzeichnisse in einem bestimmten Ordner / Verzeichnis abrufen.

Also zum Beispiel:

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

Ich würde erwarten, eine Reihe von:

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

Oder das obige mit dem Pfad, wenn es so serviert wurde ...

Gibt es also schon etwas, um das oben Genannte zu tun?

Antworten:


463

Hier ist eine kürzere, synchrone Version dieser Antwort , die alle Verzeichnisse (versteckt oder nicht) im aktuellen Verzeichnis auflisten kann:

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)

Update für Node 10.10.0+

Wir können die neue withFileTypesOption verwenden readdirSync, um den zusätzlichen lstatSyncAnruf zu überspringen :

const { readdirSync } = require('fs')

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

14
Vorsicht, Sie benötigen den absoluten Pfad, um den Dateistat zu erhalten. require('path').resolve(__dirname, file)
Silom

9
@pilau es funktioniert einfach nicht mit einem relativen Pfad, deshalb musst du ihn normalisieren. Dafür können Sie path.resolve verwenden.
Silom

1
@rickysullivan: Sie müssten die Antwort durchlaufen und path.resolve (srcpath, Ordnername) für jeden Ordner in
jarodsmk

5
Da Sie es6 verwenden:const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
pmrotule

2
Beachten Sie, dass dies unterbrochen wird, wenn das Verzeichnis Symlinks enthält. Verwenden Sie lstatSyncstattdessen.
Dave

103

Dank der Syntaxfunktionen von JavaScript ES6 (ES2015) ist es ein Liner:

Synchrone Version

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

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

Asynchrone Version für Node.js 10+ (experimentell)

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
Wenn Sie etwas in eine einzelne Zeile zwingen, ist es weniger lesbar und daher weniger erwünscht.
Rlemon

7
Sie können jede Anweisung in eine Zeile einfügen, das heißt nicht, dass Sie es sollten.
Kevin B

4
Upvoted. Ich bin mir nicht sicher, warum die Leute dies abgelehnt haben. Ein Liner ist schlecht, aber comon, Sie können es verschönern, wie Sie möchten. Der Punkt ist, dass Sie etwas aus dieser Antwort gelernt haben
Aamir Afridi

27
Upvoted. Obwohl es berechtigte Kritik ist, dass es über mehrere Zeilen verteilt werden sollte. Diese Antwort zeigt den semantischen Vorteil der neuen Syntax, aber ich denke, die Leute werden davon abgelenkt, wie sie in eine Zeile gesteckt wurde. Wenn Sie diesen Code über dieselbe Anzahl von Zeilen wie die akzeptierte Antwort verteilen, ist er immer noch ein prägnanterer Ausdruck desselben Mechanismus. Ich denke, diese Antwort hat einen gewissen Wert, aber vielleicht verdient sie eine Bearbeitung der akzeptierten Antwort und keine eindeutige Antwort.
Iron Savior

23
Ist das ihr ernst? Dies ist eine sehr gute und gültige Antwort! Eine Zeile, plagiierend - wünschte, es gäbe eine Möglichkeit, miese Ausreden herunterzustimmen. Das ist keine Raketenwissenschaft. Es ist eine sehr einfache und dumme Operation! Sei nicht wortreich! Bekommt meine +1 sicher!
Mrchief

36

Listen Sie Verzeichnisse mit einem Pfad auf.

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

1
Dieser ist ziemlich einfach
Paula Fleck

Endlich eine, die ich verstehen kann
FourCinnamon0

21

Rekursive Lösung

Ich bin hierher gekommen, um nach einem Weg zu suchen, um alle Unterverzeichnisse und alle ihre Unterverzeichnisse usw. abzurufen. Aufbauend auf der akzeptierten Antwort schrieb ich Folgendes:

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

Dies ist genau das, wonach ich gesucht habe und es scheint großartig zu funktionieren, außer dass jeder Pfad außer dem ersten so aussieht: "src \\ pages \\ partials \\ button" anstelle dieses "src / pages / partials / button" . Ich habe diesen schmutzigen Fix hinzugefügt: var res = getDirectoriesRecursive (srcpath); res = res.map (Funktion (x) {return x.replace (/ \\ / g, "/")}); console.log (res);
PaulB

1
Ein weniger schmutziger Weg, dies zu tun, ist path.normalize (). nodejs.org/api/path.html#path_path_normalize_path
Patrick McElhaney

Sie geben das übergeordnete Verzeichnis zurück, was nicht wünschenswert ist. Ich würde getDirectoriesRecursive umgestalten, um dies zu verhindern: if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; }
Nadav

10

Dies sollte es tun:

CoffeeScript (synchronisieren)

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

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

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
Wenn dies für ein Produktionssystem gilt, möchten Sie die synchronen fsMethoden unbedingt vermeiden .
Aaron Dufour

20
Hinweis für Neulinge, die diese Antwort lesen: Dies ist CoffeeScript, nicht JavaScript (mein Freund hat mich verwirrt angeschrieben und gefragt, warum JavaScript plötzlich semantische Leerzeichen hat).
DallonF

1
@nicksweet Kannst du das in JS konvertieren?
Mikemaccana

1
Es gibt einige offensichtliche Probleme mit dieser Antwort: Es gibt keine Fehlerbehandlung; (Die Rückrufsignatur sollte sein (err, dirs)); Bei Vorhandensein von Punktdateien oder Ordnern wird es nicht zurückgerufen. es ist anfällig für alle Rennbedingungen; Es kann zurückrufen, bevor alle Einträge überprüft wurden.
1j01

21
Ug, die Leute müssen aufhören, die Sync-API zu verunglimpfen. Das Bestimmen, ob eine Synchronisierungsversion verwendet werden soll oder nicht, wird nicht durch "Produktion" bestimmt. Ad nauseum zu wiederholen, dass die asynchronen APIs besser sind und die Synchronisierung ohne Kontext schlecht ist, ist einfach nicht korrekt. Ich wünschte, die JS-Community würde aufhören, dies zu verbreiten. Die Synchronisierung ist einfacher (yay), blockiert jedoch die Nachrichtenschleife (boo). Verwenden Sie also keine Synchronisierungs-APIs auf einem Server, auf dem Sie nicht blockieren möchten, sondern verwenden Sie sie in einem Build-Skript, beispielsweise dort, wo dies keine Rolle spielt. </ rant>
hcoverlambda

6

Wenn Sie externe Bibliotheken verwenden können, können Sie diese auch verwenden filehound. Es unterstützt Rückrufe, Versprechen und Synchronisierungsanrufe.

Versprechen verwenden:

const Filehound = require('filehound');

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

Rückrufe verwenden:

const Filehound = require('filehound');

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

    console.log(subdirectories);
  });

Anruf synchronisieren:

const Filehound = require('filehound');

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

console.log(subdirectories);

Weitere Informationen (und Beispiele) finden Sie in den Dokumenten: https://github.com/nspragg/filehound

Haftungsausschluss: Ich bin der Autor.


5

Mit node.js version> = v10.13.0 gibt fs.readdirSync ein Array von fs.Dirent- Objekten zurück, wenn die withFileTypesOption auf gesetzt ist true.

So können Sie verwenden,

const fs = require('fs')

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

guter Punkt, aber .filter(c => c.isDirectory())wäre einfacher als zu verwendenreduce()
Emmanuel Touzery

Ja, aber filter gibt ein Array von fs.Dirent-Objekten zurück, die Verzeichnisse sind. Das OP wollte Namen von Verzeichnissen.
Mayur

1
Es stimmt, ich würde .filter(c => c.isDirectory()).map(c => c.name)den reduceAnruf trotzdem vorziehen .
Emmanuel Touzery

ich weiß, worauf du hinauswillst. Ich denke, SO-Leser können anhand ihres Anwendungsfalls entscheiden. Ich würde sagen, dass das Durchlaufen eines In-Memory-Arrays im Vergleich zum E / A des Lesens von der Festplatte im Overhead vernachlässigbar sein sollte, selbst wenn Sie von der SSD lesen, aber wie üblich, wenn man sich wirklich interessiert, können sie messen.
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));``

4

Verwenden von fs-extra, das die asynchronen fs-Aufrufe verspricht, und der neuen Async-Syntax zum Warten:

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

Für eine asynchrone Version von getDirectories benötigen Sie dazu das asynchrone Modul :

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

Diese Antwort verwendet keine Blockierungsfunktionen wie readdirSyncoder statSync. Es verwendet weder externe Abhängigkeiten noch befindet es sich in den Tiefen der Callback-Hölle.

Stattdessen verwenden wir moderne JavaScript-Funktionen wie Promises und async-awaitSyntax. Asynchrone Ergebnisse werden parallel verarbeitet. nicht nacheinander -

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

Ich werde ein Beispielpaket installieren und dann unsere Funktion testen -

$ npm install ramda
$ node

Mal sehen, wie es funktioniert -

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

Mit einem verallgemeinerten Modul können Parallelwir die Definition von dirs- vereinfachen

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

Das Paralleloben verwendete Modul war ein Muster, das aus einer Reihe von Funktionen extrahiert wurde, um ein ähnliches Problem zu lösen. Weitere Erläuterungen finden Sie in den entsprechenden Fragen und Antworten .


1

CoffeeScript-Version dieser Antwort mit korrekter Fehlerbehandlung:

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

Kommt auf Async an

Alternativ können Sie hierfür ein Modul verwenden! (Es gibt Module für alles. [Zitieren erforderlich])


1

Wenn Sie alle asyncVersionen verwenden müssen. Sie können so etwas haben.

  1. Notieren Sie die Verzeichnislänge und verwenden Sie sie als Indikator, um festzustellen, ob alle asynchronen Statistikaufgaben abgeschlossen sind.

  2. Wenn die Aufgaben für den asynchronen Status abgeschlossen sind, wurde der gesamte Dateistatus überprüft. Rufen Sie daher den Rückruf auf

Dies funktioniert nur, solange Node.js ein einzelner Thread ist, da davon ausgegangen wird, dass keine zwei asynchronen Tasks gleichzeitig den Zähler erhöhen.

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

Wie Sie sehen können, handelt es sich hierbei um einen "Tiefe zuerst" -Ansatz, der zur Rückrufhölle führen kann und nicht ganz "funktionsfähig" ist. Die Leute versuchen, dieses Problem mit Promise zu lösen, indem sie die asynchrone Aufgabe in ein Promise-Objekt einbinden.

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

Guter Code! Aber ich denke, es sollte "Filter out files:" im Promise.all-Block sein, da es prüft, ob es sich um eine Datei handelt und diese protokolliert. :)
Bikal Nepal

1

Verwenden Sie das Pfadmodul fs 、, um den Ordner zu erhalten. diese verwenden Versprechen. Wenn Sie die Füllung erhalten, können Sie isDirectory () in isFile () ändern. Nodejs - fs - fs.Stats . Zuletzt können Sie den Dateinamen, den Dateinamen und so weiter abrufen

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

Aus der Überprüfungswarteschlange: Darf ich Sie bitten, Ihrer Antwort einen weiteren Kontext hinzuzufügen. Nur-Code-Antworten sind schwer zu verstehen. Es wird sowohl dem Fragesteller als auch zukünftigen Lesern helfen, wenn Sie Ihrem Beitrag weitere Informationen hinzufügen können.
help-info.de

1

Die vollständig asynchrone Version mit ES6, nur native Pakete, fs.promises und async / await, führt Dateivorgänge parallel aus:

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

Getestet funktioniert es gut.


0

Nur für den Fall, dass jemand anderes von einer Websuche hierher kommt und Grunt bereits in seiner Abhängigkeitsliste hat, wird die Antwort darauf trivial. Hier ist meine Lösung:

/**
 * 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

Ein weiterer rekursiver Ansatz

Vielen Dank an Mayur, der mich kennengelernt hat withFileTypes. Ich habe folgenden Code geschrieben, um Dateien eines bestimmten Ordners rekursiv abzurufen. Es kann leicht geändert werden, um nur Verzeichnisse zu erhalten.

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

funktionale Programmierung

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)
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.