디렉토리 nodejs 내의 모든 디렉토리 가져 오기


260

나는 이것이 간단한 일이기를 바랐지만 그렇게할만한 것을 찾을 수 없었습니다.

주어진 폴더 / 디렉토리에있는 모든 폴더 / 디렉토리를 가져 오려고합니다.

예를 들어 :

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

다음과 같은 배열을 얻을 것으로 예상됩니다.

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

또는 위의 경로가 제공된 방법이라면 ...

위의 작업을 수행하기 위해 이미 존재하는 것이 있습니까?

답변:


463

다음 은 현재 디렉토리의 모든 디렉토리 (숨겨 지거나 숨겨지지 않은)를 나열 할 수 있는 이 대답 의 더 짧고 동기화 된 버전입니다 .

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)

14
파일 통계를 얻으려면 절대 경로가 필요합니다. require('path').resolve(__dirname, file)
Silom

9
@ pilau 상대 경로와는 작동하지 않으므로 정규화해야합니다. 이를 위해 path.resolve를 사용할 수 있습니다.
실롬

1
@rickysullivan : 응답을 반복하고 내부의 각 폴더에 대해 path.resolve (srcpath, foldername)를 사용해야합니다.
jarodsmk

5
es6을 사용하고 있기 때문에 :const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
pmrotule

2
디렉토리에 심볼릭 링크가 있으면 중단됩니다. lstatSync대신 사용하십시오 .
Dave

103

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
}

30
무언가를 한 줄에 강제로 넣으면 읽기가 어렵고 덜 바람직합니다.
rlemon

7
한 줄에 모든 명령을 맞출 수 있다고해서 꼭 그래야하는 것은 아닙니다.
Kevin B

4
공감. 사람들이 왜 이것을 다운 토트했는지 확실하지 않습니다. 하나의 라이너는 나쁘지만 코몬은 원할 때 아름답게 만들 수 있습니다. 요점은 당신이이 답변으로부터 무언가를 배웠다는 것입니다
Aamir Afridi

27
공감. 더 많은 선으로 퍼져 나가야한다는 비판은 유효합니다. 이 답변은 새로운 구문의 의미 론적 이점을 보여 주지만 사람들이 한 줄로 채워지는 방식에 혼란스러워하고 있다고 생각합니다. 이 코드를 허용되는 답변과 동일한 수의 행에 분산 시키면 여전히 동일한 메커니즘의 간결한 표현입니다. 나는이 답변이 가치가 있다고 생각하지만 아마도 별개의 답변이 아닌 수용 된 답변을 편집해야합니다.
아이언 구세주

23
너희들 진심이야? 이것은 완벽하게 정확하고 유효한 답변입니다! 표절 한 줄로, 변명스러운 변명을 내리는 방법이 있었으면 좋겠다. 이것은 로켓 과학이 아닙니다. 매우 간단하고 멍청한 작업입니다! 그것을 위해 장황하지 마십시오! +1을 확실히 얻습니다!
Mr. No.

36

경로를 사용하여 디렉토리를 나열하십시오.

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

1
이것은 매우 간단하다
Paula Fleck

마지막으로 이해할 수있는 것
FourCinnamon0

21

재귀 솔루션

나는 모든 하위 디렉토리와 모든 하위 디렉토리 등을 얻는 방법을 찾기 위해 여기에 왔습니다. 수용 된 답변을 바탕으로 다음 과 같이 썼습니다.

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

이것은 내가 찾던 것인데 첫 번째 경로를 제외한 모든 경로가 다음과 같이 나타나는 것을 제외하고는 훌륭하게 작동하는 것 같습니다 : "src / pages / partials / buttons"대신 "src \\ pages \\ partials \\ buttons" . 이 더티 픽스를 추가했습니다 : var res = getDirectoriesRecursive (srcpath); res = res.map (function (x) {return x.replace (/ \\ / g, "/")}); console.log (res);
PaulB

1
덜 더러운 방법은 path.normalize ()입니다. nodejs.org/api/path.html#path_path_normalize_path
Patrick McElhaney 2016

바람직하지 않은 상위 ​​디렉토리를 리턴하고 있습니다. 이를 방지하기 위해 getDirectoriesRecursive를 리팩토링합니다. if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; }
Nadav

10

이것은해야합니다 :

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 (비동기)

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)

자바 스크립트 (비동기)

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
이것이 프로덕션 시스템을위한 것이더라도 동기식 fs방법 을 피하고 싶을 것 입니다.
Aaron Dufour

20
이 답변을 읽는 초보자에게 주목하십시오 : 이것은 JavaScript가 아닌 CoffeeScript입니다 (제 친구는 왜 JavaScript에 갑자기 의미 공백이 있는지 묻는 혼란스러워했습니다).
DallonF

1
@nicksweet 이것을 JS로 변환 할 수 있습니까?
mikemaccana

1
이 답변에는 눈부신 문제가 있습니다. 오류 처리가 없습니다. (콜백 서명은이어야 함 (err, dirs)); 도트 파일이나 폴더가있는 곳에서는 다시 호출하지 않습니다. 모든 경쟁 조건에 영향을 받기 쉽습니다. 모든 항목을 확인하기 전에 다시 전화를 걸 수 있습니다.
1j01

21
사람들은 동기화 API를 비방하는 것을 그만 둘 필요가 있습니다. 동기화 버전의 사용 여부는 "제작"에 의해 결정되지 않습니다. 또한 비동기 API가 더 좋고 동기화가 컨텍스트가 없으면 나쁘다는 반복적 인 광고 구역이 정확하지 않습니다. JS 커뮤니티가 이것을 발표하지 않기를 바랍니다. 동기화는 더 간단하지만 (yay) 메시지 루프 (boo)를 차단합니다. 따라서 차단하고 싶지 않은 서버에서는 동기화 API를 사용하지 말고 빌드 스크립트에서 자유롭게 사용하십시오. </ rant>
hcoverlambda

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

면책 조항 : 나는 저자입니다.


5

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
}, [])

좋은 점은 있지만, .filter(c => c.isDirectory())사용하는 것보다 간단 할 것reduce()
엠마누엘 Touzery을

예. 그러나 필터는 디렉토리 인 fs.Dirent 객체의 배열을 반환합니다. OP는 디렉토리 이름을 원했습니다.
Mayur

1
사실, 나는 여전히 전화 .filter(c => c.isDirectory()).map(c => c.name)보다 선호합니다 reduce.
Emmanuel Touzery

너의 의도를 알 겠어. 나는 독자들이 유스 케이스에 따라 결정할 수 있다고 생각합니다. SSD에서 읽는 경우에도 디스크에서 읽는 I / O와 비교하여 메모리 내 어레이를 반복하는 것은 오버 헤드에서 무시할 수 있어야하지만 실제로 관심이 있다면 평소와 같이 측정 할 수 있다고 말하고 싶습니다.
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

비동기 fs 호출을 약속하는 fs-extra 및 새로운 대기 비동기 구문 사용 :

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

그리고 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);
  });
}

2

이 답변은 readdirSync또는 과 같은 차단 기능을 사용하지 않습니다 statSync. 외부 의존성을 사용하지 않으며 콜백 지옥의 깊이에서 스스로를 찾지 않습니다.

대신 약속 및 async-await구문 과 같은 최신 JavaScript 편의를 사용 합니다. 그리고 비동기 결과는 병렬로 처리됩니다. 순차적으로-

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위에서 사용한 모듈은 유사한 문제를 해결하기위한 함수의 세트로부터 추출 된 패턴이었다. 자세한 설명은 관련 Q & A를 참조하십시오 .


1

적절한 오류 처리와 함께이 답변 의 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

비동기에 따라 다름

또는 이것을 위해 모듈을 사용하십시오! (모든 것을위한 모듈이 있습니다. [인용 필요])


1

모든 async버전 을 사용해야하는 경우 이런 식으로 할 수 있습니다.

  1. 디렉토리 길이를 기록하고이를 표시기로 사용하여 모든 비동기 상태 작업이 완료되었는지 여부를 알려줍니다.

  2. 비동기 통계 작업이 완료되면 모든 파일 통계가 확인되었으므로 콜백을 호출하십시오.

두 개의 비동기 작업이 동시에 카운터를 증가시키지 않는다고 가정하기 때문에 Node.js가 단일 스레드 인 경우에만 작동합니다.

'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 개체에 래핑하여 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);
    });
});

좋은 코드! 그러나 파일인지 확인하고 로깅 할 때 Promise.all 블록에서 "파일 필터링 :"이어야한다고 생각합니다. :)
Bikal Nepal

1

fs 、 path 모듈을 사용 하면 폴더를 얻을 수 있습니다. 이 약속을 사용합니다. 채우기, 당신의 캔 변경하여 얻을 것이다 경우 isDirectory ()) (ISFILE를 FS - - Nodejs를 fs.Stats .At을 마지막으로, 당신은 file'name의 file'extname을 얻을 수있는 등 Nodejs --- 경로

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

검토 대기열에서 : 답변과 관련하여 컨텍스트를 더 추가해 주시기 바랍니다. 코드 전용 답변은 이해하기 어렵습니다. 게시물에 더 많은 정보를 추가 할 수 있다면 독자와 미래 독자 모두에게 도움이됩니다.
help-info.de

1

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

테스트를 거쳤습니다.


0

다른 사람이 웹 검색으로 여기에 와서 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);
}

0

또 다른 재귀 접근법

에 대해 알게 된 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)
    }
}, [])

0

기능적 프로그래밍

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)
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.