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 d'un dossier/répertoire donné.
Donc 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 que ça a été servi ...
Alors, existe-t-il déjà quelque chose pour faire ce qui précède?
Voici une version plus courte et synchrone de cette réponse qui peut lister tous les répertoires (cachés ou non) dans le 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)
Grâce à JavaScript ES6 (ES2015), la syntaxe est simple:
** 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
}
Liste des répertoires en utilisant un chemin.
function getDirectories(path) {
return fs.readdirSync(path).filter(function (file) {
return fs.statSync(path+'/'+file).isDirectory();
});
}
Je suis venu ici à la recherche d'un moyen d'obtenir tous les sous-répertoires et tous leurs sous-répertoires, etc. En me basant sur le 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))];
}
Cela devrait le faire:
CoffeeScript (sync)
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 (asynchrone)
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}));
}
}
});
}
Sinon, 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.
Utilisation 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 exemples), consultez la documentation: https://github.com/nspragg/filehound
Disclaimer: je suis l'auteur.
Utilisation de fs-extra, qui promet les appels async fs, ainsi que la nouvelle syntaxe async wait:
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("/")
Avec la version node.js> = v10.13.0, fs.readdirSync retournera un tableau de fs.Dirent objects si l’option withFileTypes
est définie sur true
.
Pour que vous puissiez utiliser,
const fs = require('fs')
const directories = source => fs.readdirSync(source, {
withFileTypes: true
}).reduce((a, c) => {
c.isDirectory() && a.Push(c.name)
return a
}, [])
Et pour une version async de getDirectories, vous avez besoin du module async 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);
});
}
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));``
Cette réponse n'utilise pas de fonctions de blocage telles que readdirSync
ou statSync
. Il n'utilise pas de dépendances externes et ne se trouve pas dans les profondeurs de l'enfer de rappel.
Au lieu de cela, nous utilisons des fonctionnalités JavaScript modernes telles que les syntaxes Promises et async-await
. 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 paquet, puis tester notre fonction -
$ npm install ramda
$ node
Voyons voir que ç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 module Parallel
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, voir ceci Questions/Réponses liées .
Si vous devez utiliser toutes les versions async
. Vous pouvez avoir quelque chose comme ça.
Enregistrez la longueur du répertoire et l'utilisez comme indicateur pour indiquer si toutes les tâches de statistiques asynchrones sont terminées.
Si les tâches de statistiques asynchrones sont terminées, toutes les statistiques de fichier ont été vérifiées. Appelez le rappel.
Cela ne fonctionnera que tant que Node.js sera un seul thread, car cela 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 constater, il s’agit d’une approche de «profondeur d’abord» qui pourrait entraîner un rappel instable et qui n’est pas tout à fait «fonctionnelle». Les gens essaient de résoudre ce problème avec Promise en encapsulant la tâche async 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);
});
});
utiliser fs 、 chemin module peut obtenir le dossier. cette promesse d'utilisation. Si vous voulez obtenir le remplissage, vous pouvez changer isDirectory () en isFile ()Nodejs - fs - fs.Stats . Enfin, vous pouvez obtenir le fichier nom_fichier '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);
});
});
Version CoffeeScript de cette réponse , avec une gestion des erreurs appropriée:
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 asynchrone
Sinon, utilisez un module pour cela! (Il existe des modules pour tout. [Citation nécessaire])
Version entièrement async avec ES6, seuls les packages natifs, fs.promises et async/wait, effectuent des opérations de fichier 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é, ça marche bien.
Merci à Mayur de me connaître à propos de withFileTypes
. J'ai écrit le code suivant pour obtenir des fichiers d'un dossier particulier de manière récursive. Il peut être facilement modifié pour obtenir uniquement 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)
}
}, [])
Juste au cas où quelqu'un d'autre finirait ici par une recherche sur le Web et que Grunt soit déjà dans sa liste de dépendances, la réponse à cette question devient 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);
}
Personnellement, je déclarerais la fonction stat hors de la boucle - quelque chose comme ceci:
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 se plaint sinon et vous créez peut-être un nouvel appel de fonction à chaque passage de boucle.
Async/attend la variante:
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;
}
Je recommande également d’utiliser fs-extra au lieu de fs ..
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)
}
}