[node.js] 비어 있지 않은 디렉토리를 제거하십시오.

내 노드 응용 프로그램에서 일부 파일이있는 디렉토리를 제거해야하지만 fs.rmdir빈 디렉토리에서만 작동합니다. 어떻게해야합니까?



답변

이를위한 모듈이 있습니다 rimraf( https://npmjs.org/package/rimraf ). 그것은 같은 기능을 제공합니다rm -Rf

비동기 사용법 :

var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });

사용법 동기화 :

rimraf.sync("/some/directory");


답변

폴더를 동 기적으로 제거하려면

const fs = require('fs');
const Path = require('path');

const deleteFolderRecursive = function(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach((file, index) => {
      const curPath = Path.join(path, file);
      if (fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
};


답변

fsNode.js를 사용 하는 대부분의 사람들 은 파일을 다루는 “유닉스 방식”에 가까운 함수를 원합니다. fs-extra 를 사용하여 모든 멋진 물건을 가져 왔습니다.

fs-extra는 vanilla Node.js fs 패키지에 포함되지 않은 메소드를 포함합니다. mkdir -p, cp -r 및 rm -rf와 같은.

더 좋은 점은 fs-extra 가 기본 fs를 대체하는 것입니다. fs의 모든 메소드는 수정되지 않고 첨부됩니다. fs를 fs-extra로 대체 할 수 있음을 의미합니다 .

// this can be replaced
const fs = require('fs')

// by this
const fs = require('fs-extra')

그런 다음 폴더를 다음과 같이 제거 할 수 있습니다.

fs.removeSync('/tmp/myFolder');
//or
fs.remove('/tmp/myFolder', callback);


답변

2019 년 기준 …

현재 Node.js를 12.10.0 , fs.rmdirSync지원 recursive당신이 마지막으로 할 수 있도록 옵션 :

fs.rmdirSync(dir, { recursive: true });

recursive옵션은 전체 디렉토리를 재귀 적으로 삭제합니다.


답변

@oconnecp ( https : //.com/a/25069828/3027390 ) 에서 수정 된 답변

크로스 플랫폼 환경을 개선하기 위해 path.join을 사용합니다. 그러므로 꼭 요구하십시오.

var path = require('path');

또한 함수의 이름을 rimraf;)으로 바꿨습니다 .

/**
 * Remove directory recursively
 * @param {string} dir_path
 * @see https://stackoverflow.com/a/42505874/3027390
 */
function rimraf(dir_path) {
    if (fs.existsSync(dir_path)) {
        fs.readdirSync(dir_path).forEach(function(entry) {
            var entry_path = path.join(dir_path, entry);
            if (fs.lstatSync(entry_path).isDirectory()) {
                rimraf(entry_path);
            } else {
                fs.unlinkSync(entry_path);
            }
        });
        fs.rmdirSync(dir_path);
    }
}


답변

나는 보통 오래된 실을 부활시키지 않지만 여기에 많은 이탈이 있으며 rimraf 대답은 이것들 모두에게 지나치게 복잡해 보입니다.

현대 노드 (> = v8.0.0)에서 첫 번째로 노드 코어 모듈 만 사용하여 프로세스를 단순화하고 완전히 비 동기화하며 파일의 링크를 동시에 5 줄의 함수로 동시에 병렬화하고 가독성을 유지할 수 있습니다.

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
  }));
  await rmdir(dir);
};

또 다른 참고로, 경로 순회 공격에 대한 보호 는이 기능에 적합하지 않기 때문에

  1. 단일 책임 원칙 (Single Responsibility Principle) 에 따라 범위를 벗어납니다 .
  2. 이 함수가 아닌 호출자가 처리해야합니다 . 이것은 명령 행과 유사 rm -rf하며 인수를 취하고 사용자가 rm -rf /요청하면 이를 허용합니다 . rm프로그램 자체 를 보호하지 않는 것은 스크립트의 책임입니다 .
  3. 이 기능은 참조 프레임이 없기 때문에 이러한 공격을 확인할 수 없습니다. 또한 경로 통과를 비교하기위한 참조를 제공 할 의도의 컨텍스트를 갖는 호출자의 책임입니다.
  4. sym-links .isDirectory()falsesym-links와 마찬가지로 걱정 되지 않으며 되풀이되지 않습니다.

마지막 으로,이 재귀가 실행되는 동안 적절한 시점에 항목 중 하나 가이 스크립트 외부 에서 연결 해제되거나 삭제 된 경우 재귀 오류가 발생할 수있는 드문 경쟁 조건 이 있습니다. 이 시나리오는 대부분의 환경에서 일반적이지 않으므로 간과 될 수 있습니다. 그러나 필요한 경우 (일부 사례의 경우) 다음과 같이 좀 더 복잡한 예제를 통해이 문제를 완화 할 수 있습니다.

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
    return task.catch(error => ({ error }));
  }));
  results.forEach(result => {
    // Ignore missing files/directories; bail on other errors
    if (result && result.error.code !== 'ENOENT') throw result.error;
  });
  await rmdir(dir);
};

편집 :isDirectory() 기능을 만듭니다 . 마지막에 실제 디렉토리를 제거하십시오. 누락 된 재귀를 수정하십시오.


답변

@SharpCoder의 비동기 버전은 다음과 같습니다.

const fs = require('fs');
const path = require('path');

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};