[node.js] node.js fs.readdir 재귀 디렉토리 검색

fs.readdir을 사용하여 비동기 디렉토리 검색에 대한 아이디어가 있습니까? 재귀를 도입하고 읽을 다음 디렉토리로 디렉토리 읽기 기능을 호출 할 수 있음을 알고 있지만 비동기 적이 지 않을까 걱정됩니다.

어떤 아이디어? 나는 node-walk 를 살펴 보았지만 readdir처럼 배열의 파일 만 제공하지는 않습니다. 이기는 하지만

다음과 같은 출력을 찾고 있습니다 …

['file1.txt', 'file2.txt', 'dir/file3.txt']



답변

이것을 달성하는 기본적으로 두 가지 방법이 있습니다. 비동기 환경에서는 직렬 및 병렬의 두 가지 루프가 있음을 알 수 있습니다. 직렬 루프는 다음 반복으로 넘어 가기 전에 하나의 반복이 완료 될 때까지 기다립니다. 이렇게하면 루프의 모든 반복이 순서대로 완료됩니다. 병렬 루프에서는 모든 반복이 동시에 시작되고 하나가 다른 것보다 먼저 완료 될 수 있지만 직렬 루프보다 훨씬 빠릅니다. 따라서이 경우 결과를 완료하고 반환하는 한 (순서를 원하지 않는 한) 워크가 완료되는 순서가 중요하지 않기 때문에 병렬 루프를 사용하는 것이 좋습니다.

병렬 루프는 다음과 같습니다.

var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var pending = list.length;
    if (!pending) return done(null, results);
    list.forEach(function(file) {
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            if (!--pending) done(null, results);
          });
        } else {
          results.push(file);
          if (!--pending) done(null, results);
        }
      });
    });
  });
};

직렬 루프는 다음과 같습니다.

var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var i = 0;
    (function next() {
      var file = list[i++];
      if (!file) return done(null, results);
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            next();
          });
        } else {
          results.push(file);
          next();
        }
      });
    })();
  });
};

그리고 홈 디렉토리에서 테스트하려면 (경고 : 홈 디렉토리에 많은 것이 있으면 결과 목록이 커집니다) :

walk(process.env.HOME, function(err, results) {
  if (err) throw err;
  console.log(results);
});

편집 : 개선 된 예.


답변

이것은 노드 8에서 제공되는 약속, 활용 / 약점, 구조 조정, 비동기 대기, 맵 + 축소 등을 포함하여 최대의 새로운 유행어 기능을 사용하여 동료가 무엇을 알아 내려고 할 때 머리를 긁습니다. 진행되고있다.

노드 8 이상

외부 의존성이 없습니다.

const { promisify } = require('util');
const { resolve } = require('path');
const fs = require('fs');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);

async function getFiles(dir) {
  const subdirs = await readdir(dir);
  const files = await Promise.all(subdirs.map(async (subdir) => {
    const res = resolve(dir, subdir);
    return (await stat(res)).isDirectory() ? getFiles(res) : res;
  }));
  return files.reduce((a, f) => a.concat(f), []);
}

용법

getFiles(__dirname)
  .then(files => console.log(files))
  .catch(e => console.error(e));

노드 10.10+

더 많은 whizbang과 함께 노드 10 이상에 대해 업데이트되었습니다.

const { resolve } = require('path');
const { readdir } = require('fs').promises;

async function getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  const files = await Promise.all(dirents.map((dirent) => {
    const res = resolve(dir, dirent.name);
    return dirent.isDirectory() ? getFiles(res) : res;
  }));
  return Array.prototype.concat(...files);
}

노드 11.15.0부터는 파일 배열을 병합하는 files.flat()대신 사용할 수 있습니다 Array.prototype.concat(...files).

노드 11 이상

모든 사람의 머리를 완전히 날려 버리려면 async iterators 를 사용하여 다음 버전을 사용할 수 있습니다 . 소비자는 정말 시원 할뿐만 아니라 한 번에 하나씩 결과를 가져 와서 실제로 큰 디렉토리에 더 적합하게 만들 수 있습니다.

const { resolve } = require('path');
const { readdir } = require('fs').promises;

async function* getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  for (const dirent of dirents) {
    const res = resolve(dir, dirent.name);
    if (dirent.isDirectory()) {
      yield* getFiles(res);
    } else {
      yield res;
    }
  }
}

리턴 유형이 이제 약속이 아닌 비동기 반복기이므로 사용법이 변경되었습니다.

(async () => {
  for await (const f of getFiles('.')) {
    console.log(f);
  }
})()

누군가 관심이 있다면 https://qwtel.com/posts/software/async-generators-in-the-wild/에서 비동기 반복기에 대해 더 많이 썼습니다.


답변

누군가가 유용하다고 생각하는 경우 동기 버전을 작성했습니다.

var walk = function(dir) {
    var results = [];
    var list = fs.readdirSync(dir);
    list.forEach(function(file) {
        file = dir + '/' + file;
        var stat = fs.statSync(file);
        if (stat && stat.isDirectory()) {
            /* Recurse into a subdirectory */
            results = results.concat(walk(file));
        } else {
            /* Is a file */
            results.push(file);
        }
    });
    return results;
}

팁 : 필터링시 더 적은 리소스를 사용합니다. 이 함수 자체 내에서 필터링하십시오. 예를 들어 results.push(file);아래 코드로 교체하십시오 . 필요에 따라 조정하십시오.

    file_type = file.split(".").pop();
    file_name = file.split(/(\\|\/)/g).pop();
    if (file_type == "json") results.push(file);


답변

A. 파일 모듈을 살펴보십시오 . walk라는 기능이 있습니다.

file.walk (시작, 콜백)

(null, dirPath, dirs, files)를 전달하여 각 디렉토리에 대한 콜백을 호출하여 파일 트리를 탐색합니다.

이것은 당신을위한 것일 수 있습니다! 그리고 네, 그것은 비동기입니다. 그러나 필요한 경우 전체 경로를 직접 집계해야한다고 생각합니다.

B. 대안, 심지어 내가 좋아하는 것 중 하나 : 유닉스 find를 사용하십시오 . 이미 프로그래밍 된 무언가가 왜 다시 프로그래밍됩니까? 아마도 정확히 필요한 것은 아니지만 여전히 체크 아웃 할 가치가 있습니다.

var execFile = require('child_process').execFile;
execFile('find', [ 'somepath/' ], function(err, stdout, stderr) {
  var file_list = stdout.split('\n');
  /* now you've got a list with full path file names */
});

Find에는 폴더가 거의 변경되지 않는 한 후속 검색을 매우 빠르게 수행하는 멋진 내장 캐싱 메커니즘이 있습니다.


답변

또 다른 멋진 npm 패키지는 glob 입니다.

npm install glob

매우 강력하며 모든 되풀이 요구를 충족해야합니다.

편집하다:

나는 실제로 glob에 완전히 만족하지 않았으므로 readdirp을 만들었습니다 .

API가 파일과 디렉토리를 재귀 적으로 찾고 특정 필터를 쉽게 적용 할 수 있다고 확신합니다.

설명서 를 읽고 내용을 보다 잘 이해하고 다음을 통해 설치하십시오.

npm install readdirp


답변

해당 작업을 수행하기 위해 node-glob 을 사용하는 것이 좋습니다 .

var glob = require( 'glob' );

glob( 'dirname/**/*.js', function( err, files ) {
  console.log( files );
});


답변

npm 패키지를 사용하려면 렌치 가 좋습니다.

var wrench = require("wrench");

var files = wrench.readdirSyncRecursive("directory");

wrench.readdirRecursive("directory", function (error, files) {
    // live your dreams
});

편집 (2018) :
최근에 읽은 사람 : 2015 년에이 패키지가 더 이상 사용되지 않습니다.

wrench.js는 더 이상 사용되지 않으며 꽤 오랫동안 업데이트되지 않았습니다. 추가 파일 시스템 작업을 수행하려면 fs-extra사용하는 것이 좋습니다 .