IT박스

node.js rmdir은 재귀 적입니까?

itboxs 2020. 10. 31. 09:30

node.js rmdir은 재귀 적입니까? 비어 있지 않은 디렉토리에서 작동합니까?


fs.rmdir에 대한 문서 는 매우 짧으며 디렉토리가 비어 있지 않을 때 rmdir의 동작을 설명하지 않습니다.

Q :이 API를 사용하여 비어 있지 않은 디렉토리를 삭제하려고하면 어떻게됩니까?


짧은 대답 : node.js fs.rmdir() 는 POSIX를 호출합니다 rmdir(). 빈 디렉토리를 제거하거나 오류를 반환합니다 . 주어진 경우 호출은 콜백 함수를 호출하고 오류를 예외로 전달합니다.

여기서 문제는 node.js 문서가 POSIX를 참조한다는 것입니다 .

Node.js를 API 문서 파일 시스템 소개 말합니다 :

파일 I / O는 표준 POSIX 기능에 대한 간단한 래퍼로 제공됩니다.

이것은 질문을 거의 중복으로 바꿉니다. POSIX API / 함수 목록이 있습니까?

에 대한 설명 fs.rmdir은 간결하지만 충분합니다.

비동기 rmdir (2).

다음 rmdir(2)은 .NET 용 설명서에 대한 암시 적 참조 rmdir() system call입니다. 여기서 숫자 (2)는 커널 인터페이스를 포함하는 매뉴얼 페이지의 섹션 2를 나타내는 오래된 유닉스 매뉴얼 페이지 규칙입니다.


그러한 일을 위해 타사 라이브러리를 사용했지만 더 우아한 솔루션을 찾을 수 없었습니다. 그래서 결국 npm-module rimraf를 사용했습니다 .

그것을 설치하십시오

npm install rimraf

또는 그것을 설치하고 'package.json'에 저장하십시오 (다른 저장 옵션은 npm-install 문서 에서 찾을 수 있습니다 )

npm install --save rimraf

그런 다음 다음을 수행 할 수 있습니다.

rmdir = require('rimraf');
rmdir('some/directory/with/files', function(error){});

또는 Coffeescript에서 :

rmdir = require 'rimraf'
rmdir 'some/directory/with/files', (error)->

나는이 문제에 대해 정확히 썼다 .

아래의 이전 솔루션은 간단하지만 선호되지 않습니다. 다음 기능은 동기식 솔루션입니다. 비동기가 선호 될 수 있습니다.

deleteFolderRecursive = function(path) {
    var files = [];
    if( fs.existsSync(path) ) {
        files = fs.readdirSync(path);
        files.forEach(function(file,index){
            var curPath = path + "/" + file;
            if(fs.lstatSync(curPath).isDirectory()) { // recurse
                deleteFolderRecursive(curPath);
            } else { // delete file
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
};

[편집] 심볼릭 링크 오류를 방지하기 위해 stat 대신 lstat 추가

[이전 솔루션]

이에 대한 내 솔루션은 구현하기가 매우 쉽습니다.

var exec = require('child_process').exec,child;
child = exec('rm -rf test',function(err,out) { 
  console.log(out); err && console.log(err); 
});

이 페이지에서는 얇아졌지만 기본 아이디어는 간단합니다. 명령 줄에서 'rm -r'을 실행합니다. 앱이 다양한 유형의 OS에서 실행되어야하는 경우이를 함수에 넣고 if / else / switch를 사용하여 처리합니다.

모든 응답을 처리하고 싶을 것입니다. 하지만 아이디어는 충분히 간단합니다.


이 많은 답변 중 작은 점이지만 지적하는 것이 좋습니다.

개인적으로 (일반적으로) 작업을 수행하기 위해 사용 가능한 라이브러리가있는 경우 이미 존재하는 라이브러리를 사용하는 것을 선호합니다. 이미 존재하는 것을 취한다는 것은 저에게, 특히 오픈 소스 세계에서 이미 존재하는 것을 사용하고 개선하는 것을 의미합니다. 이는 혼자서하는 것보다 더 나은 결과를 얻을 수 있습니다 (다른 사람이 가지고있는 것을 개선하고 있습니다). 끝난).

이 경우 작은 검색을 통해 fs-extra 모듈을 발견했습니다. 이 모듈rimraf재귀 적으로 디렉토리를 제거해야하는 필요성에 대한 답변 이기도합니다 (분명히 비동기 및 동기화 버전). 또한, github에서 많은 별을 얻었으며 현재 관리되고있는 것처럼 보입니다.이 두 가지 조건은 필요에 대한 답변과 함께 저를 위해 (거의 거의) 갈 길을 만듭니다.


fs.rmdir입니다 하지 재귀.

대신 모든 파일과 디렉토리를 찾기 위해 readdirp 와 같은 재귀 적 fs.readdir 모듈을 사용할 수 있습니다 . 그런 다음 모든 파일과 모든 디렉토리를 제거합니다.

더 간단한 솔루션을 위해 rimraf를 살펴 보십시오 .


Node.js를 v12.10.0 도입 recursive에 옵션을 fs.rmdir. 으로 fs.mkdir지원 같은 옵션 v10.12.0 이후, 모두 결정 및 제거 디렉토리는 재귀 적으로 실행할 수 있습니다.

$ node --experimental-repl-await

# without recursive option -> error
> await fs.promises.mkdir('foo/bar')
Thrown:
[Error: ENOENT: no such file or directory, mkdir 'foo/bar'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'mkdir',
  path: 'foo/bar'
}

# with recursive option -> success
> await fs.promises.mkdir('foo/bar', { recursive: true })
undefined

# without recursive option -> error
> await fs.promises.rmdir('foo')
Thrown:
[Error: ENOTEMPTY: directory not empty, rmdir 'foo'] {
  errno: -66,
  code: 'ENOTEMPTY',
  syscall: 'rmdir',
  path: 'foo'
}

# with recursive option -> success
> await fs.promises.rmdir('foo', { recursive: true })
undefined

child_process.execFile을 사용하면 더 빠릅니다 .

NodeJS 문서 :

child_process.execFile은 subshell을 실행하지 않고 지정된 파일을 직접 실행한다는 점을 제외하면 child_process.exec ()와 유사합니다.

작동합니다. 모방rm -rf DIR...

var child = require('child_process');

var rmdir = function(directories, callback) {
    if(typeof directories === 'string') {
        directories = [directories];
    }
    var args = directories;
    args.unshift('-rf');
    child.execFile('rm', args, {env:process.env}, function(err, stdout, stderr) {
            callback.apply(this, arguments);
    });
};

// USAGE
rmdir('dir');
rmdir('./dir');
rmdir('dir/*');
rmdir(['dir1', 'dir2']);

편집 : 나는 이것이 크로스 플랫폼이 아니며 Windows에서 작동하지 않는다는 것을 인정해야합니다


다음은 promise와 함께 작동하는 비동기 재귀 버전입니다. 나는 'Q'라이브러리를 사용하지만 누구나 약간의 변경 (예 : '실패'기능)을 사용합니다.

이를 활용하기 위해 우리는 몇 가지 핵심 노드 함수, 즉 fs.stat, fs.readdir, fs.unlink 및 fs.rmdir 주위에 몇 가지 간단한 래퍼를 만들어 약속 친화적으로 만들어야합니다.

여기 있습니다:

function getStat(fpath) {
  var def = Q.defer();
  fs.stat(fpath, function(e, stat) {
    if (e) { def.reject(); } else { def.resolve(stat); }
  });
  return def.promise;
}

function readdir(dirpath) {
  var def = Q.defer();
  fs.readdir(dirpath, function(e, files) {
    if (e) { def.reject(e); } else { def.resolve(files); }
  });
  return def.promise;
}

function rmFile(fpath) {
  var def = Q.defer();
  fs.unlink(fpath, function(e) { if(e) { def.reject(e); } else { def.resolve(fpath); }});
  return def.promise;
}

function rmDir(fpath) {
  var def = Q.defer(); 
  fs.rmdir(fpath, function(e) { if(e) { def.reject(e); } else { def.resolve(fpath); }});
  return def.promise;
}

그래서 다음은 재귀 rm 함수입니다.

var path = require('path');

function recursiveDelete(fpath) {
  var def = Q.defer();

  getStat(fpath)
  .then(function(stat) {
    if (stat.isDirectory()) {
      return readdir(fpath)
      .then(function(files) {
        if (!files.length) { 
          return rmDir(fpath);
        } else {
          return Q.all(files.map(function(f) { return recursiveDelete(path.join(fpath, f)); }))
          .then(function() { return rmDir(fpath); });
        }
      }); 
    } else {
      return rmFile(fpath);
    }
  })
  .then(function(res) { def.resolve(res); })
  .fail(function(e) { def.reject(e); })
  .done();
  return def.promise;
}

이것이 소스에 대한 다이빙을하기에 좋은 변명이라고 생각했습니다.)

내가 알 수 fs.rmdir있듯이 unistd.h의 rmdir 함수에 바인딩되어 있습니다. 로부터 RMDIR에 대한 POSIX man 페이지 :

rmdir () 함수는 경로로 이름이 지정된 디렉토리를 제거해야합니다. 디렉토리는 빈 디렉토리 인 경우에만 제거됩니다.

디렉토리가 빈 디렉토리가 아니면 rmdir ()은 실패하고 errno를 [EEXIST] 또는 [ENOTEMPTY]로 설정합니다.


올바른 "아니오"응답 외에도 rimraf 패키지는 재귀 삭제 기능을 제공합니다. 모방 rm -rf합니다. 또한 공식적 으로 Ubuntu에서 패키지화 했습니다.


나는이 정확히 손의 질문에 대답하지 않습니다 실현,하지만이 사람은 (그것이 나에게했을 것이다!) 미래에 여기 검색에 유용 할 수 있습니다 생각 : 나는 하나가 반복적으로 삭제 할 수있는 작은 조각 만든 에만 빈을 디렉토리 . 디렉토리 (또는 하위 디렉토리)에 내용이 있으면 그대로 남습니다.

var fs = require("fs");
var path = require("path");

var rmdir = function(dir) {
    var empty = true, list = fs.readdirSync(dir);
    for(var i = list.length - 1; i >= 0; i--) {
        var filename = path.join(dir, list[i]);
        var stat = fs.statSync(filename);

        if(filename.indexOf('.') > -1) {
            //There are files in the directory - we can't empty it!
            empty = false;
            list.splice(i, 1);
        }
    }

    //Cycle through the list of sub-directories, cleaning each as we go
    for(var i = list.length - 1; i >= 0; i--) {
        filename = path.join(dir, list[i]);
        if (rmdir(filename)) {
            list.splice(i, 1);
        }
    }

    //Check if the directory was truly empty
    if (!list.length && empty) {
        console.log('delete!');
        fs.rmdirSync(dir);
        return true;
    }
    return false;
};

https://gist.github.com/azaslavsky/661020d437fa199e95ab


내가 보는 대부분의 예제는 노드에서 폴더 구조를 재귀 적으로 삭제하는 동기식 구현입니다.

실제로 잘 작동하지 않는 비동기식 몇 가지도 보았습니다.

나는 완전히 비동기적인 것을 작성하고 사용합니다 : https://gist.github.com/yoavniran/adbbe12ddf7978e070c0


이 함수는 지정한 디렉터리 또는 파일을 동 기적으로 재귀 적으로 삭제합니다.

var path = require('path');

function deleteRecursiveSync(itemPath) {
    if (fs.statSync(itemPath).isDirectory()) {
        _.each(fs.readdirSync(itemPath), function(childItemName) {
            deleteRecursiveSync(path.join(itemPath, childItemName));
        });
        fs.rmdirSync(itemPath);
    } else {
        fs.unlinkSync(itemPath);
    }
}

다음과 같은 경우이 함수의 동작을 테스트하지 않았습니다.

  • 항목이 존재하지 않거나
  • 항목을 삭제할 수 없습니다 (예 : 권한 문제).

Node.js의 재귀 적 제거 디렉토리

Node.js fs 모듈에는 디렉토리와 그 내용을 재귀 적으로 제거하는 방법이 없다는 것이 밝혀졌습니다. 대신 디렉토리 구조를 살펴보고 개별 파일과 빈 디렉토리와 같은 원자 항목을 제거해야합니다. 그래서 Takuo Kihira 가 JavaScript로 만든 https://gist.github.com/2367067 에서 멋진 요점을 찾아서 CoffeeScript 버전을 만들기로 결정했습니다.


동기화 제거로 인해 파일 또는 디렉토리가 사용 중이면 오류가 발생하므로 오류가 발생하지 않도록 시도했습니다.

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

var dumpDirs = function (dir, name, cb) {
fs.readdir(dir, function (err, files) {
    var dirs = [],
    filePath, i = 0, l = files.length;
    for (var i = 0; i < l; i++) {
        filePath = path.join(dir, files[i]);
        var stats = fs.lstatSync(filePath);
        if (stats.isDirectory()) {
            if (files[i].indexOf(name) != -1) {
                dirs.push({
                    startOn: new Date(stats.ctime),
                    instance: files[i],
                    name: name
                })
            }
        }
    }
    cb(dirs);
});
}

var removeDir = function (dir, callback) {
fs.readdir(dir, function (err, files) {
    c = files.length;

    (function remfile(i, cb) {
        if (i >= c)
            return cb();
        var p = path.join(dir, files[i])
        fs.unlink(p, function (err) {
            if (err) console.log(err);
            remfile(i + 1, cb)
        });

    })(0, function () {
        fs.rmdir(dir, function (err) {
            callback()
        });
    });

    //for (var i = 0; i < c; i++) {
    //    fs.unlinkSync(path.join(dir, files[i]));
    //};


});
}
dumpDirs(maindir, function (dirs) {

if (dirs && dirs.length > 0) {
    (function rem(i, cb) {
        if (i >= dirs.length) {
            return cb();
        }
        var folder = path.join(dump, dirs[i].instance);
        removeDir(folder, function () {
            rem(i + 1, cb);
        });
    })(0, function () {
        callback();
    })
}
else {
    callback();
}
});

다음은 폴더를 재귀 적으로 삭제하는 fluentnode위해 만든 커피 스크립트 프로토 타입 함수입니다.

String::folder_Delete_Recursive = ->
  path = @.toString()
  if path.exists()
    for file in path.files()
      curPath = path.path_Combine(file)
      if curPath.is_Folder()
        curPath.folder_Delete_Recursive()
      else
        curPath.file_Delete()
    fs.rmdirSync(path);

  return path.not_Exists()

여기에 테스트가 있습니다.

it 'folder_Create and folder_Delete' , ->
  tmpDir = "./".temp_Name_In_Folder()
  expect(tmpDir.folder_Exists()).to.be.false
  expect(tmpDir.folder_Create()).to.equal(tmpDir.realPath())
  expect(tmpDir.folder_Exists()).to.be.true
  expect(tmpDir.folder_Delete()).to.be.true
  expect(tmpDir.folder_Exists()).to.be.false

it 'folder_Delete_Recursive' , ->
  tmpDir = "./"   .temp_Name_In_Folder().folder_Create()
  tmpFile = tmpDir.temp_Name_In_Folder().file_Create()
  expect(tmpDir.folder_Delete_Recursive()).to.be.true

rmdirSync의 깔끔한 동기 버전.

/** 
 * use with try ... catch ...
 * 
 * If you have permission to remove all file/dir
 * and no race condition and no IO exception...
 * then this should work 
 *
 * uncomment the line 
 *   if(!fs.exists(p)) return 
 * if you care the inital value of dir, 
 * 
 */
var fs = require('fs')
var path = require('path')

function rmdirSync(dir,file){
  var p = file? path.join(dir,file):dir;
  // if(!fs.exists(p)) return 
  if(fs.lstatSync(p).isDirectory()){
    fs.readdirSync(p).forEach(rmdirSync.bind(null,p))
    fs.rmdirSync(p)
  }
  else fs.unlinkSync(p)
}

그리고 병렬 IO, 비동기 버전의 rmdir. (빠름)

/**
 * NOTE: 
 * 
 * If there are no error, callback will only be called once.
 * 
 * If there are multiple errors, callback will be called 
 * exactly as many time as errors occur. 
 * 
 * Sometimes, this behavior maybe useful, but users 
 * should be aware of this and handle errors in callback. 
 * 
 */

var fs = require('fs')
var path = require('path')

function rmfile(dir, file, callback){
  var p = path.join(dir, file)
  fs.lstat(p, function(err, stat){
    if(err) callback.call(null,err)
    else if(stat.isDirectory()) rmdir(p, callback)
    else fs.unlink(p, callback)
  })
}

function rmdir(dir, callback){
  fs.readdir(dir, function(err,files){
    if(err) callback.call(null,err)
    else if( files.length ){
      var i,j
      for(i=j=files.length; i--; ){
        rmfile(dir,files[i], function(err){
          if(err) callback.call(null, err)
          else if(--j === 0 ) fs.rmdir(dir,callback)
        })
      }
    }
    else fs.rmdir(dir, callback)
  })
}

어쨌든 순차 IO를 원하고 콜백은 정확히 한 번 호출됩니다 (성공 또는 첫 번째 오류 발생). 이 rmdir을 위의 것으로 대체하십시오. (느리게)

function rmdir(dir, callback){
  fs.readdir(dir, function(err,files){
    if(err) callback.call(null,err)
    else if( files.length ) rmfile(dir, files[0], function(err){
      if(err) callback.call(null,err)
      else rmdir(dir, callback)
    })
    else fs.rmdir(dir, callback)
  })
}

모두 node.js 에만 의존 하며 이식 가능해야합니다.


이 게시물은 Google에서 최고의 답변을 얻었지만 어떤 답변도 다음과 같은 솔루션을 제공하지 않습니다.

  • 동기화 기능을 사용하지 않습니다.

  • 외부 라이브러리가 필요하지 않습니다.

  • bash를 직접 사용하지 않습니다.

다음은 async설치된 노드 이외의 다른 것을 가정하지 않는 솔루션입니다.

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

function rm(path){  
    return stat(path).then((_stat) => {                                   

    if(_stat.isDirectory()){                                                                                                                                                                                                                          
      return ls(path)                                                                                                                                                                                                                                   
        .then((files) => Promise.all(files.map(file => rm(Path.join(path, file)))))
       .then(() => removeEmptyFolder(path));                                                                                                                                                                                                 
    }else{                                                                                                                                                                                                                                            
      return removeFileOrLink(path);                                                                                                                                                                                                            
    }   });
                                                                                                                                                                                                                                              function removeEmptyFolder(path){                                     

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.rmdir(path, function(error){                                                                                                                                                                                                                   
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done("ok");                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                function removeFileOrLink(path){                                      

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.unlink(path, function(error){                                                                                                                                                                                                                  
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done("ok");                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }

                                                                                                                                                                                                                                                function ls(path){                                                    

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.readdir(path, function (error, files) {                                                                                                                                                                                                        
        if(error) return err(error)                                                                                                                                                                                                                   
        return done(files)                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                function stat(path){                                                  

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.stat(path, function (error, _stat) {                                                                                                                                                                                                           
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done(_stat);                                                                                                                                                                                                                       
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          } }

@geedew의 답변에 따라.

다음은의 비동기 구현입니다 rm -r(즉, 파일 또는 디렉토리에 경로를 전달할 수 있음). 저는 숙련 된 nodejs 개발자가 아니며 제안이나 건설적인 비판에 감사드립니다.

var fs = require('fs');

function ResultsCollector (numResultsExpected, runWhenDone) {
    this.numResultsExpected = numResultsExpected,
    this.runWhenDone = runWhenDone;
    this.numResults = 0;
    this.errors = [];

    this.report = function (err) {
        if (err) this.errors.push(err);
        this.numResults++;
        if (this.numResults == this.numResultsExpected) {
            if (this.errors.length > 0) return runWhenDone(this.errors);
            else return runWhenDone();
        }
    };
}

function rmRasync(path, cb) {
    fs.lstat(path, function(err, stats) {
        if (err && err.code == 'ENOENT') return cb(); // doesn't exist, nothing to do
        else if (err) {
            return cb(err);
        }
        if (stats.isDirectory()) {
            fs.readdir(path, function (err, files) {
                if (err) return cb(err);
                var resultsCollector = new ResultsCollector(files.length, function (err) {
                    if (err) return cb(err);
                    fs.rmdir(path, function (err) {
                        if (err) return cb(err);
                        return cb();
                    });
                });
                files.forEach(function (file) {
                    var filePath = path + '/' + file;
                    return rmRasync(filePath, function (err) {
                        return resultsCollector.report(err);
                    });
                });
            });
        }
        else { // file.
            // delete file or link
            fs.unlink(path, function (err) {
                if (err) return cb(err);
                return cb();
            });
        }
    });
};

다음과 같이 호출하십시오.

rmRasync('/path/to/some/file/or/dir', function (err) {
    if (err) return console.error('Could not rm', err);
    // else success
});

Surprisingly verbose and bad answers here...

To delete a non-empty directory on most systems:

import * as cp from 'child_process';

const dir = '/the/dir/to/remove';

const k = cp.spawn('bash');

k.stdin.end(`rm -rf "${dir}"`);

k.once('exit', code => {
   // check the exit code
   // now you are done
});

this will work on MacOS and Linux, but it might not work on some Windows OS.

참고URL : https://stackoverflow.com/questions/12627586/is-node-js-rmdir-recursive-will-it-work-on-non-empty-directories