node.js - 違い - サーバー サイド javascript node js 入門
node.js rmdirは再帰的ですか? 空でないディレクトリでも動作しますか? (13)
@ 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
});
fs.rmdirのドキュメントは非常に短く、ディレクトリが空でない場合のrmdirの動作については説明しません。
Q :このAPIを使用して空でないディレクトリを削除しようとするとどうなりますか?
Node.jsの再帰的な削除ディレクトリ
Node.js fsモジュールにはディレクトリとその内容を再帰的に削除するメソッドがないことが判明しました。 代わりに、ディレクトリ構造を調べて、アトミックな項目、つまり個々のファイルと空のディレクトリを削除する必要があります。 だから私は、JavaScriptで作られたhttps://gist.github.com/2367067木尾卓雄のすばらしさを見つけ、CoffeeScript版を作ることにしました:
下の私の以前の解決策は単純ですが、それは好まれません。 次の機能は、同期ソリューションです。 asyncが優先される可能性があります。
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 /スイッチを使用して処理します。
すべての応答を処理したいと思うでしょう。 考え方は簡単です。
ここでは、約束事と連動する非同期再帰バージョンがあります。 私は 'Q'ライブラリを使用していますが、誰かがいくつかの変更(例えば、 '失敗'機能など)を行うことになります。
これを利用するには、いくつかのコアNode関数、すなわち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;
}
このようなことのためにサードパーティのライブラリを使用していましたが、私はより洗練されたソリューションを考え出すことができませんでした。 だから私はnpm-module rimrafを使い終わった。
インストールする
npm install rimraf
またはそれをインストールして 'package.json'に保存してください(他の保存オプションはnpm-install docsにあります)
npm install --save rimraf
次に、次の操作を実行できます。
rmdir = require('rimraf');
rmdir('some/directory/with/files', function(error){});
またはCoffeescriptで:
rmdir = require 'rimraf'
rmdir 'some/directory/with/files', (error)->
この答えの中にちょっとした小さな点がありますが、それを指摘するのは良いことだと思います。
個人的に(そして一般的に)私は、既存のライブラリがあれば、そのライブラリを利用することをお勧めします。 既存のものを取るということは、私にとって、特にオープンソースの世界では、既存のものを使用し改善することを意味します。これは自分自身で行うよりも良い結果に終わる可能性があります(私は、完了)。
この場合、小さな検索で私はrimraf
-extraというモジュールを見つけました。これはrimraf
となり、再帰的なディレクトリ(非同期と同期のバージョンがあるようです)を削除する必要性への答えになります。 さらに、githubには数多くの星があり、現時点では手に入れられているようです。この2つの条件は、必要性に答えるだけでなく、私にとって(少しでも)行く方法にもなります。
この関数は、指定したディレクトリまたはファイルを同期的に再帰的に削除します。
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);
}
}
私はこの関数の動作をテストしていません:
- アイテムが存在しないか、または
- そのアイテムは削除できません(パーミッションの問題など)。
その時にファイルやディレクトリが使用されていると、同期の削除によってエラーが発生するため、安全に失敗しました。
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();
}
});
それはより速い child_process.execFileを使用してください。
child_process.execFileはchild_process.exec()と似ていますが、*はサブシェルを実行せず、指定されたファイルを直接実行する点が異なります。
これは機能します。 rm -rf DIR...
模倣してい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上で動作しません
私が見ているほとんどの例は、ノード内のフォルダ構造を再帰的に削除する同期実装です。
実際にうまく動作しない非同期のものもいくつか見てきました。
私は完全に非同期のものを書いて使いました: https://gist.github.com/yoavniran/adbbe12ddf7978e070c0 : https://gist.github.com/yoavniran/adbbe12ddf7978e070c0
私はこれが正確に問題に答えているわけではないことを認識していますが、これは将来これを探している人にとって役に立つかもしれないと思います(私にとってはそうだったでしょう):私は空を唯一再帰的に削除できる小さなスニペットを作ったディレクトリ 。 ディレクトリ(またはその子孫ディレクトリ)内にコンテンツがある場合、そのディレクトリはそのままです。
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;
};
var fs = require('fs');
fs.delR = function(dir){
var s = fs.lstatSync(dir);
if(s.isFile())
fs.unlinkSync(dir);
if(!s.isDirectory())
return;
var fileArr = fs.readdirSync(dir);
for(f in fileArr)
fs.delR(dir+'/'+fileArr[f]);
fs.rmdirSync(dir);
}