Dizin nodejs içindeki tüm dizinleri alın


260

Bunun basit bir şey olacağını umuyordum, ama orada yapacak bir şey bulamıyorum.

Sadece belirli bir klasör / dizin içindeki tüm klasörleri / dizinleri almak istiyorum.

Yani mesela:

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

Ben bir dizi almak beklenir:

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

Ya da yukarıdaki yol bu şekilde sunulmuşsa ...

Peki yukarıdakileri yapmak için zaten bir şey var mı?

Yanıtlar:


464

Bu cevabın geçerli dizindeki tüm dizinleri (gizli veya değil) listeleyebilen daha kısa, senkronize bir sürümü :

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)

Düğüm 10.10.0+ için güncelleştirme

Ekstra aramayı atlamak için yeni withFileTypesseçeneğini kullanabiliriz :readdirSynclstatSync

const { readdirSync } = require('fs')

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

14
Dikkatli olun, stat dosyasını almak için mutlak yola ihtiyacınız var. require('path').resolve(__dirname, file)
Silom

9
@pilau göreceli bir yolla çalışmıyor, bu yüzden normalleştirmeniz gerekiyor. Bunun için path.resolve kullanabilirsiniz.
Silom

1
@rickysullivan: Her klasör iç için yanıt ve kullanım path.resolve (srcpath, klasöradı) yinelemenize gerekiyordu
jarodsmk

5
Es6 kullandığınız için:const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
pmrotule

2
Dizinde sembolik bağlantılar varsa bu koptuğunu unutmayın. lstatSyncBunun yerine kullanın .
Dave,

103

JavaScript ES6 (ES2015) sözdizimi özellikleri sayesinde bir astardır:

Senkron versiyon

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

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

Node.js 10+ için eşzamansız sürüm (deneysel)

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
bir şeyi tek bir satıra zorlamak onu daha az okunabilir ve dolayısıyla daha az istenir hale getirir.
rlemon

7
Herhangi bir talimatı bir hatta sığdırabilirsiniz, yapmanız gerektiği anlamına gelmez.
Kevin B

4
Upvoted. İnsanların bunu neden düşürdüğünden emin değilim. Bir astar kötü ama comon, istediğiniz gibi güzelleştirebilirsiniz. Mesele şu ki, bu cevaptan bir şeyler öğrendiniz
Aamir Afridi

27
Upvoted. Her ne kadar geçerli bir eleştiri olsa da, daha fazla satıra yayılması gerektiği. Bu cevap, yeni sözdiziminin semantik avantajını göstermektedir, ancak bence insanlar bir satıra nasıl sıkıştırıldığına dikkatleri dağıtıyor. Bu kodu kabul edilen cevapla aynı sayıda satıra yayırsanız, yine de aynı mekanizmanın daha özlü bir ifadesidir. Bu cevabın bir değeri olduğunu düşünüyorum, ama belki de ayrı bir cevaptan ziyade kabul edilen cevabın bir düzenlemesi olmayı hak ediyor.
Demir Kurtarıcı

23
Siz ciddi misiniz? Bu gayet iyi ve geçerli bir cevap! Bir satır, intihal - berbat mazeretleri azaltmanın bir yolu olsaydı. Bu roket bilimi değil. Bu çok basit ve aptal bir işlem! Bunun uğruna ayrıntılı olmayın! + 1'mi kesin alır!
Mrchief

36

Dizinleri bir yol kullanarak listeleyin.

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

1
Bu oldukça basit
Paula Fleck

Sonunda anlayabiliyorum
FourCinnamon0

22

Özyinelemeli çözüm

Buraya tüm alt dizinleri ve tüm alt dizinlerini vb. Almanın bir yolunu bulmak için geldim. Kabul edilen cevaba dayanarak şunu yazdım:

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

Bu tam olarak aradığım şeydi ve ilk işin dışındaki her yol şu şekilde görünmesi dışında harika çalışıyor gibi görünüyor: "src / pages / partials / buttons" yerine "src \\ pages \\ partials \\ düğmeleri" . Bu kirli düzeltmeyi ekledim: var res = getDirectoriesRecursive (srcpath); res = res.map (işlev (x) {dönüş x.replace (/ \\ / g, "/")}); console.log (S);
PaulB

2
Bunu yapmanın daha az kirli bir yolu path.normalize () yöntemidir. nodejs.org/api/path.html#path_path_normalize_path
Patrick McElhaney

İstenmeyen üst dizini döndürüyorsunuz. Bunu önlemek için getDirectoriesRecursive refactor olur: if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; }
Nadav

10

Bunu yapmalı:

CoffeeScript (senkronizasyon)

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 (zaman uyumsuz)

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 (zaman uyumsuz)

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
Bu bir üretim sistemi için olsa da, senkron fsyöntemlerden gerçekten kaçınmak istersiniz .
Aaron Dufour

20
Bu yanıtı okuyan yeni başlayanlara dikkat edin: bu JavaScript değil, CoffeeScript'tir (arkadaşım beni karıştırdı ve JavaScript'in neden aniden anlamsal boşluğa sahip olduğunu soran karışıklık içinde mesajlaştı).
DallonF

1
@nicksweet Bunu JS'ye dönüştürebilir misiniz?
mikemaccana

1
Bu cevapla ilgili bazı göze çarpan sorunlar var: herhangi bir hata işlemesi yok; (geri arama imzası olmalıdır (err, dirs)); nokta dosya veya klasörlerin varlığında geri çağırmaz; tüm yarış koşullarına açıktır; tüm girişleri kontrol etmeden önce geri arayabilir.
1j01

21
Ug, insanların senkronizasyon API'sini kötülemeyi bırakmaları gerekiyor. Senkronizasyon versiyonunun kullanılıp kullanılmayacağının belirlenmesi, "üretim" olmasıyla belirlenmez. Ayrıca, zaman uyumsuz apilerin daha iyi ve eşzamanlamanın kötü olduğunu, bağlam olmadan tekrarlanması doğru değildir. Keşke JS topluluğu bunu duyurmayı bırakacaktı. Senkronizasyon daha basittir (yay) ancak mesaj döngüsünü (boo) engeller. Bu nedenle, senkronize etmek istemediğiniz bir sunucuda sync api'leri kullanmayın, ancak bunları önemli olmayan bir yapı komut dosyasında kullanmaktan çekinmeyin. </rant>
hcoverlambda

6

Alternatif olarak, harici kütüphaneleri kullanabiliyorsanız, filehound . Geri çağrıları, vaatleri ve çağrıları senkronize etmeyi destekler.

Sözleri kullanma:

const Filehound = require('filehound');

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

Geri çağrıları kullanma:

const Filehound = require('filehound');

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

    console.log(subdirectories);
  });

Aramayı senkronize et:

const Filehound = require('filehound');

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

console.log(subdirectories);

Daha fazla bilgi (ve örnekler) için dokümanlara göz atın: https://github.com/nspragg/filehound

Feragatname: Ben yazarım.


5

Node.js sürüm> = v10.13.0 ile fs.readdirSync bir dizi döndürür fs.Dirent eğer nesneleri withFileTypesseçeneğin ayarlandığı true.

Böylece kullanabilirsiniz,

const fs = require('fs')

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

iyi bir nokta, ama .filter(c => c.isDirectory())kullanmak daha basit olurdureduce()
Emmanuel Touzery

Evet, ancak filtre dizin olan fs.Dirent nesneleri dizisini döndürür. OP dizinlerin isimlerini istedi.
Mayur

1
Doğru, yine .filter(c => c.isDirectory()).map(c => c.name)de reducegörüşmeyi tercih ederdim .
Emmanuel Touzery

senin değinmek istediğin noktayı anlıyorum. sanırım SO okuyucular kullanım durumlarına göre karar verebilirler. bir bellek içi dizisi üzerinde döngü SSD okuyorsanız bile diskten okuma I / O ile karşılaştırıldığında havai ihmal edilebilir olduğunu söyleyebilirim, ama her zamanki gibi gerçekten umursamıyor, onlar ölçebilirsiniz.
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

Async fs çağrılarını vaat eden fs-extra kullanmak ve yeni async sözdizimini bekliyor:

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

Ve getDirectories'in eşzamansız bir sürümü için bunun için eşzamansız modüle ihtiyacınız vardır :

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

Bu cevap, readdirSyncveya gibi engelleme işlevlerini kullanmaz statSync. Dış bağımlılıkları kullanmaz ve kendini geri arama cehenneminin derinliklerinde bulmaz.

Bunun yerine Promises ve async-awaitsözdizimleri gibi modern JavaScript kolaylıklarını kullanıyoruz . Ve eşzamansız sonuçlar paralel olarak işlenir; sırayla değil -

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

Bir örnek paket kuracağım ve sonra fonksiyonumuzu test edeceğim -

$ npm install ramda
$ node

Bakalım işe yarıyor -

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

Genelleştirilmiş bir modül kullanarak 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 ])
    : []

ParallelYukarıda kullanılan modül benzer bir sorunu çözmek için tasarlanmış bir fonksiyon grubu ekstrakte edilmiştir yapısıdır. Daha fazla açıklama için ilgili S ve C'ye bakın .


1

Bu hatanın doğru hata işleme özelliğine sahip CoffeeScript sürümü :

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

Zaman uyumsuzluğa bağlıdır

Alternatif olarak, bunun için bir modül kullanın! (Her şey için modüller vardır. [Alıntı gerekli])


1

Tüm asyncsürümü kullanmanız gerekiyorsa . Böyle bir şey olabilir.

  1. Dizin uzunluğunu kaydedin, tüm zaman uyumsuz istatistik görevlerinin tamamlanıp tamamlanmadığını göstermek için bir gösterge olarak kullanır.

  2. Zaman uyumsuz istatistik görevleri tamamlandıysa, tüm dosya durumu kontrol edilir, bu nedenle geri aramayı çağırın

Bu yalnızca Node.js tek bir iş parçacığı olduğu sürece çalışır, çünkü hiçbir zaman uyumsuz görevin sayacı aynı anda artıracağını varsaymaz.

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

Gördüğünüz gibi, bu bir "önce derinlik" yaklaşımıdır ve bu geri arama cehennemi ile sonuçlanabilir ve oldukça "işlevsel" değildir. İnsanlar zaman uyumsuz görevi bir Promise nesnesine sararak bu sorunu Promise ile çözmeye çalışırlar.

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

İyi kod! Ama bir dosya olup olmadığını kontrol ve günlüğe kaydetme olarak Promise.all blokta "Dosyaları dışarı filtre:" olması gerektiğini düşünüyorum. :)
Bikal Nepal

1

kullanmak fs, yol modülü klasörü var olabilir. Bu kullanım Promise. Doldurma, kutu değişim senin alacak olursa isDirectory () için ) (isFile fs - - Nodejs fs.Stats .AT son sen file'name file'extname alabilir ve böylece Nodejs --- Yolu

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

İnceleme kuyruğundan: Lütfen yanıtınıza daha fazla bağlam eklemenizi isteyebilir miyim? Yalnızca kod yanıtlarının anlaşılması zordur. Gönderinize daha fazla bilgi ekleyebilmeniz hem askerlere hem de gelecekteki okuyuculara yardımcı olacaktır.
help-info.de

1

ES6 ile tam eşzamansız sürüm, yalnızca yerel paketler, fs.promises ve async / await, dosya işlemlerini paralel olarak yapar:

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 edildi, güzel çalışıyor.


0

Herkesin bir web aramasından gelmesi ve Grunt'un zaten bağımlılık listesinde olması durumunda, bunun cevabı önemsiz hale gelir. İşte benim çözümüm:

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

Başka bir özyinelemeli yaklaşım

Beni tanıdığı için Mayur'a teşekkürler withFileTypes. Belirli bir klasördeki dosyaları yinelemeli olarak almak için aşağıdaki kodu yazdım. Yalnızca dizinleri almak için kolayca değiştirilebilir.

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

fonksiyonel programlama

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)
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.