web-dev-qa-db-fra.com

Supprimer le répertoire qui n'est pas vide

Dans mon application Node, je dois supprimer un répertoire contenant des fichiers, mais fs.rmdir ne fonctionne que sur des répertoires vides. Comment puis-je faire ceci?

186
sachin

Il existe un module pour cela appelé rimraf ( https://npmjs.org/package/rimraf ). Il fournit les mêmes fonctionnalités que rm -Rf

Async usage:

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

Sync usage:

rimraf.sync("/some/directory");
202
Morgan ARR Allen

Pour supprimer un dossier de manière synchrone

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

La plupart des personnes utilisant fs avec Node.js aimeraient des fonctions proches de la "méthode Unix" de traitement des fichiers. J'utilise fs-extra pour apporter tout ce qui est cool:

fs-extra contient des méthodes qui ne sont pas incluses dans Vanilla Node.js package fs. Tels que mkdir -p, cp -r et rm -rf.

Mieux encore, fs-extra remplace avantageusement les fs natifs. Toutes les méthodes de fs ne sont pas modifiées et y sont attachées . Cela signifie que vous pouvez remplacer fs par fs-extra:

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

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

Et puis vous pouvez supprimer un dossier de cette façon:

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

Ma réponse modifiée de @oconnecp ( https://stackoverflow.com/a/25069828/3027390 )

Utilise path.join pour une meilleure expérience multi-plateforme . Alors, n'oubliez pas de l'exiger.

var path = require('path');

Également renommé la fonction en 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);
    }
}
16
thybzi

Voici une version asynchrone de @ La réponse de 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

J'ai écrit cette fonction appelée supprimer le dossier. Il supprimera récursivement tous les fichiers et dossiers d'un emplacement. Le seul package requis est async.

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

Si vous utilisez le noeud 8+ souhaitez une asyncronie et ne souhaitez pas de dépendances externes, voici la version async/wait:

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

Je suis arrivé ici tout en essayant de surmonter la gulp et j'écris pour d'autres.

Lorsque vous souhaitez supprimer des fichiers et des dossiers à l'aide de del , vous devez ajouter /** pour une suppression récursive.

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

En général, je ne ressuscite pas d'anciens threads, mais il y a un lot sur le désabonnement ici et sans que le rimraf réponde, tout cela me semble trop compliqué.

Tout d’abord dans le nœud moderne (> = v8.0.0), vous pouvez simplifier le processus en utilisant uniquement des modules de nœud, entièrement asynchrones, et paralléliser la suppression des liens de fichiers simultanément dans une fonction de cinq lignes tout en conservant la lisibilité:

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

Sur une autre note, un garde pour attaques de traversée de chemin est inapproprié pour cette fonction car

  1. Il est hors de portée basé sur le principe de responsabilité unique .
  2. Devrait être traité par l'appelant not cette fonction. Cela ressemble à la ligne de commande rm -rf dans la mesure où cela prend un argument et permettra à l'utilisateur de rm -rf / s'il le lui demande. Il serait de la responsabilité d'un script de ne pas protéger le programme rm lui-même.
  3. Cette fonction serait incapable de déterminer une telle attaque car elle ne possède pas de cadre de référence. Encore une fois, c'est la responsabilité de l'appelant qui aurait le contexte d'intention qui lui fournirait une référence pour comparer la traversée du chemin.
  4. Les liens symboliques ne sont pas un sujet de préoccupation, car .isDirectory est false pour les liens symboliques et sont non liés dans ce mode.

Enfin et surtout, il existe une condition de concurrence rare que la récursion puisse générer une erreur si l'une des entrées était non liée ou supprimée outside ce script au bon moment au moment où cette récursion est en cours d'exécution. Étant donné que ce scénario n’est pas typique dans la plupart des environnements, il peut probablement être négligé. Toutefois, si nécessaire (pour certains cas Edge), ce problème peut être atténué avec cet exemple légèrement plus complexe:

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

La version async de @SharpCoder répond en utilisant 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);
    }
};
1
Error404

Une méthode rapide et délicate (peut-être à des fins de test) pourrait consister à utiliser directement la méthode exec ou spawn pour appeler l'appel du système d'exploitation afin de supprimer le répertoire. En savoir plus sur NodeJs child_process .

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

Les inconvénients sont:

  1. Vous dépendez du système d’exploitation sous-jacent, c’est-à-dire que la même méthode s’exécute sous unix/linux mais probablement pas sous Windows.
  2. Vous ne pouvez pas pirater le processus si des conditions ou des erreurs se produisent. Vous confiez simplement la tâche au système d'exploitation sous-jacent et attendez que le code de sortie soit renvoyé.

Avantages:

  1. Ces processus peuvent s'exécuter de manière asynchrone.
  2. Vous pouvez écouter la sortie/erreur de la commande. La sortie de la commande n'est donc pas perdue. Si l'opération n'est pas terminée, vous pouvez vérifier le code d'erreur et réessayer.
1
Rash

dossier de synchronisation supprimer avec les fichiers ou seulement un fichier.

Je ne suis ni un donateur ni un contributeur, mais je ne pouvais pas trouver une bonne solution à ce problème et je devais trouver mon chemin ... alors j'espère que vous l'apprécierez :)

Fonctionne parfaitement pour moi avec un nombre quelconque de répertoires et de sous-répertoires imbriqués. Attention pour la portée de 'this' lors de la fonction récurrente, votre implémentation peut être différente. Dans mon cas, cette fonction reste dans le retour d'une autre fonction, c'est pourquoi je l'appelle avec ceci.

    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

Il suffit d’utiliser le module rmdir ! c'est facile et simple.

1
Aminovski

Version promisifiée:

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
Clark

J'aimerais qu'il y ait un moyen de faire cela sans modules supplémentaires pour quelque chose d'aussi minuscule et commun, mais c'est le meilleur que je pourrais trouver.

Mise à jour: Devrait maintenant fonctionner sous Windows (testé Windows 10) et devrait également fonctionner sur les systèmes 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);
    });
}
1
b01

// sans utiliser de bibliothèque tierce

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

Ultra-rapide et infaillible

Vous pouvez utiliser le package lignator ( --- (https://www.npmjs.com/package/lignator ), il est plus rapide que tout code asynchrone (par exemple, rimraf) et plus infaillible (en particulier sous Windows, où la suppression de fichier n’est pas instantanée et les fichiers peuvent être verrouillés par d’autres processus).

4,36 Go de données, 28 042 fichiers, 4 217 dossiers sous Windows supprimés en 15 secondes par rapport aux 60 secondes de rimraf le ancien disque dur.

const lignator = require('lignator');

lignator.remove('./build/');
0
HankMoody

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

0
Erisan Olasheni

Une autre alternative consiste à utiliser le module fs-promise qui fournit des versions promises du module fs-extra modules.

vous pourriez alors écrire comme ceci:

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)

remarque: async/wait requiert une version récente de nodejs (7.6+)

0
Max Fichtelmann

C'est une approche utilisant promisify et deux fonctions d'aide (to et toAll) pour résoudre la promesse.

Il fait toutes les actions de manière asynchrone.

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