Удалить каталог, который не пуст


в моем узле приложения мне нужно удалить каталог, который имеет некоторые файлы, но fs.rmdir работает только с пустыми каталогами. Как я могу это сделать?

16 147

16 ответов:

есть модуль для этого называется rimraf (https://npmjs.org/package/rimraf). он обеспечивает ту же функциональность, что и rm -Rf

основное использование:

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

удалить папку синхронно

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);
  }
};

большинство людей, используя fs С узла.js хотел бы, чтобы функции были близки к "Unix-способу" работы с файлами. Я использую fs-extra чтобы принести все классные вещи:

fs-extra содержит методы, которые не включены в узел vanilla.js пакет ФС. Например, команды mkdir -Р, ПУ -р и РМ -РФ.

еще лучше, fs-extra это капля в замене для родной fs. Все методы в fs не модифицированы и прикреплены к оно. Это означает, что вы можете заменить ДФ на fs-extra:

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

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

и затем вы можете удалить папки таким образом:

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

мой измененный ответ от @oconnecp (https://stackoverflow.com/a/25069828/3027390)

использует пути.присоединяйтесь для лучшего кросс-платформенного опыта. Так что, не забудьте потребовать его.

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);
    }
}

вот асинхронная версия @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);
            });
        });
    });
};

Я написал эту функцию удалить папку. Это будет рекурсивно удалить все файлы и папки на месте. Единственный пакет, который он требует, - это асинхронность.

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)
            })
        })
    })
}

Если вы используете узел 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);
    }
}

я добрался сюда, пытаясь покончить с gulp и я пишу для дальнейшего достигает.

если вы хотите удалить файлы и папки с помощью del, вы должны добавить /** для рекурсивного удаления.

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

просто использовать модуль rmdir! это легко и просто.

обещанная версия:

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))
}

Другой альтернативой является использование 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+)

Я хотел было способ, чтобы сделать это без дополнительных модулей за какой-то мизер, но это лучшее, что я мог придумать.

обновление: Теперь должен работать на 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 sinle 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);
    });
}

быстрый и грязный способ (возможно, для проверки) может быть непосредственно использовать exec или spawn метод вызова вызова ОС для удаления каталога. Подробнее читайте на child_process NodeJs.

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

недостатки:

  1. вы зависите от базовой ОС, т. е. тот же метод будет работать в unix/linux, но, вероятно, не в windows.
  2. вы не можете захватить процесс на условия или ошибки. Вы просто передайте задачу базовой ОС и дождитесь возврата кода выхода.

преимущества:

  1. эти процессы могут выполняться асинхронно.
  2. вы можете прослушать вывод / ошибку команды, поэтому вывод команды не теряется. Если операция не завершена, можно проверить код ошибки и повторить попытку.

асинхронная версия ответа @SharpCoder с использованием fs.обещания:

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);
    }
};

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

let _dirloc = '<path_do_the_directory>'

if(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!')
    }
}

для удаления конкретного файла в папке

const fs =require('fs');

fs.unlinkSync('./newtestdir / check.txt');