web-dev-qa-db-ja.com

空でないディレクトリを削除

私のNodeアプリケーションでは、いくつかのファイルがあるディレクトリを削除する必要がありますが、 fs.rmdir は空のディレクトリでのみ動作します。これどうやってするの?

216
sachin

rimrafhttps://npmjs.org/package/rimraf )というモジュールがあります。 rm -Rfと同じ機能を提供します。

非同期の使用法:

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

同期の使用法:

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

フォルダを同期的に削除する

var fs = require('fs');
var deleteFolderRecursive = function(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).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);
  }
};
159
SharpCoder

Node.jsでfsを使う人たちのほとんどは、ファイルを扱う "Unix的な方法"に近い関数を望んでいます。私は 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);
133
Pierre Maoui

@oconnecpからの私の修正された答え( https://stackoverflow.com/a/25069828/3027390

よりよいクロスプラットフォーム体験のためにpath.joinを使用します。だから、それを要求することを忘れないでください。

var path = require('path');

Functionを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);
    }
}
18
thybzi

これは @ 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);
            });
        });
    });
};
10
Tony Brix

私はこの機能をフォルダ削除と呼びました。ある場所にあるすべてのファイルとフォルダを再帰的に削除します。それが必要とする唯一のパッケージは非同期です。

var async = require('async');

function removeFolder(location, next) {
    fs.readdir(location, function (err, files) {
        async.each(files, function (file, cb) {
            file = location + '/' + file
            fs.stat(file, function (err, stat) {
                if (err) {
                    return cb(err);
                }
                if (stat.isDirectory()) {
                    removeFolder(file, cb);
                } else {
                    fs.unlink(file, function (err) {
                        if (err) {
                            return cb(err);
                        }
                        return cb();
                    })
                }
            })
        }, function (err) {
            if (err) return next(err)
            fs.rmdir(location, function (err) {
                return next(err)
            })
        })
    })
}
9
oconnecp

もしあなたがノード8+を使っていて非同期性が欲しくて外部の依存関係が欲しくないなら、これはasync/awaitバージョンです:

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

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);
const unlink = util.promisify(fs.unlink);
const rmdir = util.promisify(fs.rmdir);

const removeDir = async (dir) => {
    try {
        const files = await readdir(dir);
        await Promise.all(files.map(async (file) => {
            try {
                const p = path.join(dir, file);
                const stat = await lstat(p);
                if (stat.isDirectory()) {
                    await removeDir(p);
                } else {
                    await unlink(p);
                    console.log(`Removed file ${p}`);
                }
            } catch (err) {
                console.error(err);
            }
        }))
        await rmdir(dir);
        console.log(`Removed dir ${dir}`);
    } catch (err) {
      console.error(err);
    }
}
6
RonZ

私はgulpを乗り越えようとしながらここにたどり着きました。

del を使用してファイルやフォルダを削除する場合は、再帰的な削除のために/**を追加する必要があります。

gulp.task('clean', function () {
    return del(['some/path/to/delete/**']);
});
3
Jin Kwon

@SharpCoderの非同期バージョンは、fs.promisesを使用して答えます。

const afs = fs.promises;

const deleteFolderRecursive = async path =>  {
    if (fs.existsSync(path)) {
        for (let entry of await afs.readdir(path)) {
            const curPath = path + "/" + entry;
            if ((await afs.lstat(curPath)).isDirectory())
                await deleteFolderRecursive(curPath);
            else await afs.unlink(curPath);
        }
        await afs.rmdir(path);
    }
};
2
Error404

私はたいてい古いスレッドを復活させることはしませんが、ここにはlotがありますが、rimrafの答えは全部私には複雑すぎるようです。

最新のNode(> = 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 });
  return Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory ? rmdir(fullPath) : unlink(fullPath);
  }));
};

別のメモでは、 パストラバーサル攻撃 に対する保護はこの機能には不適切です。

  1. それは、 単一責任原則 に基づく範囲外です。
  2. 呼び出し側によって処理される必要がありますnotこの関数。これはコマンドラインのrm -rfと似ていますが、引数を取り、要求された場合はユーザーがrm -rf /を使用できるようにします。 rmプログラム自体を保護するのはスクリプトの責任です。
  3. この関数は参照フレームがないため、このような攻撃を特定することはできません。これもまた、パストラバーサルを比較するための参考となる意図の文脈を持つことになる呼び出し側の責任です。
  4. .isDirectoryはsym-linksではfalseであり、リンク解除されずに再帰されるので、シンムリンクは問題になりません。

大事なことを言い忘れましたが、エントリの1つがリンク解除または削除された場合、この再帰が実行されている間にoutsideこのスクリプトが正しく実行されないことがあります。このシナリオはほとんどの環境では一般的ではないため、見逃される可能性があります。ただし、必要に応じて(Edgeの場合によっては)、このわずかに複雑な例を使用してこの問題を軽減できます。

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory ? rmdir(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;
  });
};
2
Sukima

非常に小さくて一般的なもののために追加のモジュールなしでこれを実行する方法があったらいいのに、これは私が思い付くことができる最高です。

更新日:Windows(テスト済みのWindows 10)上で動作し、Linux/Unix/BSD/Macシステム上でも動作するはずです。

const
    execSync = require("child_process").execSync,
    fs = require("fs"),
    os = require("os");

let removeDirCmd, theDir;

removeDirCmd = os.platform() === 'win32' ? "rmdir /s /q " : "rm -rf ";

theDir = __dirname + "/../web-ui/css/";

// WARNING: Do not specify a single file as the windows rmdir command will error.
if (fs.existsSync(theDir)) {
    console.log(' removing the ' + theDir + ' directory.');
    execSync(removeDirCmd + '"' + theDir + '"', function (err) {
        console.log(err);
    });
}
2
b01

ファイルまたはファイルのみでフォルダーを削除する同期

私は寄付者でも寄稿者でもありませんが、この問題の良い解決策を見つけることができず、自分の道を見つけなければなりませんでした...あなたがそれを好きになることを願っています:)

任意の数のネストされたディレクトリとサブディレクトリで私に最適です。関数を再帰するときの「this」の範囲に対する注意、実装は異なる場合があります。私の場合、この関数は別の関数の戻り値にとどまるため、これで呼び出しています。

    const fs = require('fs');

    deleteFileOrDir(path, pathTemp = false){
            if (fs.existsSync(path)) {
                if (fs.lstatSync(path).isDirectory()) {
                    var files = fs.readdirSync(path);
                    if (!files.length) return fs.rmdirSync(path);
                    for (var file in files) {
                        var currentPath = path + "/" + files[file];
                        if (!fs.existsSync(currentPath)) continue;
                        if (fs.lstatSync(currentPath).isFile()) {
                            fs.unlinkSync(currentPath);
                            continue;
                        }
                        if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) {
                            fs.rmdirSync(currentPath);
                        } else {
                            this.deleteFileOrDir(currentPath, path);
                        }
                    }
                    this.deleteFileOrDir(path);
                } else {
                    fs.unlinkSync(path);
                }
            }
            if (pathTemp) this.deleteFileOrDir(pathTemp);
        }
1
q212

単に rmdirモジュール を使ってください。それは簡単で簡単です。

1
Aminovski

確認版:

const fs = require('fs')
const path = require('path')
const Q = require('q')

function rmdir(dir) {
  return Q.nfcall(fs.access, dir).then(() => {
    return Q.nfcall(fs.readdir, dir)
      .then(files => files.reduce((pre, f) => pre.then(() => {
        var sub = path.join(dir, f)
        return Q.nfcall(fs.lstat, sub).then(stat => {
          if (stat.isDirectory()) return rmdir(sub)
          return Q.nfcall(fs.unlink, sub)
        })
      }), Q()))
  }, err => {})
  .then(() => Q.nfcall(fs.rmdir, dir))
}
1
clarkttfu

//サードパーティのライブラリを使用せずに

const fs = require('fs');
var FOLDER_PATH = "./dirname";
var files = fs.readdirSync(FOLDER_PATH);
files.forEach(element => {
    fs.unlinkSync(FOLDER_PATH + "/" + element);
});
fs.rmdirSync(FOLDER_PATH);
0
Amy

簡単で汚い方法(おそらくテスト用)は、execまたはspawnメソッドを直接使用してディレクトリを削除するためのOS呼び出しを実行することです。 NodeJs child_process についてもっと読んでください。

let exec = require('child_process').exec
exec('rm -Rf /tmp/*.Zip', callback)

欠点は次のとおりです。

  1. 基礎となるOSに依存しています。つまり、同じ方法がunix/linuxでも実行できますが、おそらくWindowsでは実行できません。
  2. あなたは条件やエラーでプロセスをハイジャックすることはできません。基礎となるOSにタスクを渡して、終了コードが返されるのを待つだけです。

メリット:

  1. これらのプロセスは非同期的に実行できます。
  2. あなたはコマンドの出力/エラーを監視することができます、それ故にコマンド出力は失われません。操作が完了していない場合は、エラーコードを確認して再試行してください。
0
Rash

超高速およびフェイルプルーフ

lignatorパッケージ( https://www.npmjs.com/package/lignator )を使用できます。これはどの非同期コード(たとえばrimraf)よりも高速で、特にフェイルプルーフ(特に) Windowsでは、ファイルの削除は瞬時ではなく、ファイルは他のプロセスによってロックされる可能性があります)。

4,36 GBのデータ、28,042ファイル、Windowsの4 217フォルダーが15秒で削除され、vs rimrafの60秒古いHDD。

const lignator = require('lignator');

lignator.remove('./build/');
0
HankMoody
const fs = require("fs")
const path = require("path")

let _dirloc = '<path_do_the_directory>'

if (fs.existsSync(_dirloc)) {
  fs.readdir(path, (err, files) => {
    if (!err) {
      for (let file of files) {
        // Delete each file
        fs.unlinkSync(path.join(_dirloc, file))
      }
    }
  })
  // After the 'done' of each file delete,
  // Delete the directory itself.
  if (fs.unlinkSync(_dirloc)) {
    console.log('Directory has been deleted!')
  }
}
0
Erisan Olasheni

もう1つの方法は、 fs-promise モジュールの正式版を提供する fs-extra モジュールを使用することです。

この例のように書くことができます:

const { remove, mkdirp, writeFile, readFile } = require('fs-promise')
const { join, dirname } = require('path')

async function createAndRemove() {
  const content = 'Hello World!'
  const root = join(__dirname, 'foo')
  const file = join(root, 'bar', 'baz', 'hello.txt')

  await mkdirp(dirname(file))
  await writeFile(file, content)
  console.log(await readFile(file, 'utf-8'))
  await remove(join(__dirname, 'foo'))
}

createAndRemove().catch(console.error)

注意:async/awaitは最新のnodejsバージョン(7.6+)を必要とします

0
Max Fichtelmann

これはpromisifyと2つのヘルプ関数(toとtoAll)を使って約束を解決する1つの方法です。

それはすべての行動を非同期的に行います。

const fs = require('fs');
const { promisify } = require('util');
const to = require('./to');
const toAll = require('./toAll');

const readDirAsync = promisify(fs.readdir);
const rmDirAsync = promisify(fs.rmdir);
const unlinkAsync = promisify(fs.unlink);

/**
    * @author Aécio Levy
    * @function removeDirWithFiles
    * @usage: remove dir with files
    * @param {String} path
    */
const removeDirWithFiles = async path => {
    try {
        const file = readDirAsync(path);
        const [error, files] = await to(file);
        if (error) {
            throw new Error(error)
        }
        const arrayUnlink = files.map((fileName) => {
            return unlinkAsync(`${path}/${fileName}`);
        });
        const [errorUnlink, filesUnlink] = await toAll(arrayUnlink);
        if (errorUnlink) {
            throw new Error(errorUnlink);
        }
        const deleteDir = rmDirAsync(path);
        const [errorDelete, result] = await to(deleteDir);
        if (errorDelete) {
            throw new Error(errorDelete);
        }
    } catch (err) {
        console.log(err)
    }
}; 
0
Aecio Levy