ในแอปพลิเคชั่น Node ของฉันฉันจำเป็นต้องลบไดเรกทอรีที่มีไฟล์บางไฟล์ แต่fs.rmdir
ใช้ได้เฉพาะกับไดเรกทอรีที่ว่างเปล่า ฉันจะทำสิ่งนี้ได้อย่างไร
ในแอปพลิเคชั่น Node ของฉันฉันจำเป็นต้องลบไดเรกทอรีที่มีไฟล์บางไฟล์ แต่fs.rmdir
ใช้ได้เฉพาะกับไดเรกทอรีที่ว่างเปล่า ฉันจะทำสิ่งนี้ได้อย่างไร
คำตอบ:
มีโมดูลสำหรับสิ่งนี้เรียกว่าrimraf
( https://npmjs.org/package/rimraf ) มันมีฟังก์ชั่นเดียวกับrm -Rf
การใช้Async :
var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });
การใช้งานการซิงค์ :
rimraf.sync("/some/directory");
deleteFolderRecursive
ในคำตอบต่อไปนี้?
recursive
ตัวเลือก: stackoverflow.com/a/57866165/6269864
เพื่อลบโฟลเดอร์พร้อมกัน
const fs = require('fs');
const Path = require('path');
const deleteFolderRecursive = function(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach((file, index) => {
const curPath = Path.join(path, file);
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
};
var curPath = path + "/" + file;
ด้วยvar curPath = p.join(path, file);
หากคุณได้รวมโมดูลพา ธ :var p = require("path")
path.join(dirpath, file)
ควรดีกว่าpath + "/" + file
คนส่วนใหญ่ที่ใช้fs
กับ Node.js ต้องการฟังก์ชั่นใกล้เคียงกับ "วิธี Unix" ของการจัดการกับไฟล์ ฉันใช้fs-extraเพื่อนำสิ่งดีๆมาให้:
fs-extra มีวิธีการที่ไม่รวมอยู่ในแพ็คเกจ fs ของ vanilla Node.js เช่น mkdir -p, cp -r และ rm -rf
ยิ่งไปกว่านั้นFS พิเศษคือการลดลงของ 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);
removeSync('/tmp/myFolder')
ในฐานะของNode.js 12.10.0 , fs.rmdirSync
สนับสนุนrecursive
ตัวเลือกเพื่อให้คุณจนสามารถทำได้:
fs.rmdirSync(dir, { recursive: true });
ที่recursive
ตัวเลือกลบไดเรกทอรีทั้งหมดซ้ำ
recursive: true
และลบโฟลเดอร์ที่ไม่ว่างโดยไม่มีการร้องเรียน
fs.rmdir(path[, options], callback)
หรือfs.rmdirSync(path[, options])
fs.rmdir
กำลังทำการทดลองด้วยความเสถียร 1. "Stability: 1 - Experimental คุณลักษณะนี้ไม่อยู่ภายใต้กฎ Semantic Versioning การเปลี่ยนแปลงหรือการลบที่เข้ากันได้แบบไม่ย้อนหลัง การเปิดตัวในอนาคตไม่แนะนำให้ใช้คุณสมบัตินี้ในสภาพแวดล้อมการผลิต "
คำตอบที่แก้ไขของฉันจาก @oconnecp ( https://stackoverflow.com/a/25069828/3027390 )
ใช้ path.join เพื่อประสบการณ์ข้ามแพลตฟอร์มที่ดียิ่งขึ้น ดังนั้นอย่าลืมที่จะกำหนดมัน
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);
}
}
ฉันมักจะไม่ฟื้นคืนชีพเธรดเก่า ๆ แต่มีจำนวนมากที่ปั่นป่วนที่นี่และ Sans rimraf ตอบคำถามเหล่านี้ดูเหมือนจะซับซ้อนเกินไปสำหรับฉัน
สิ่งแรกใน modern Node (> = v8.0.0) คุณสามารถทำให้กระบวนการง่ายขึ้นโดยใช้เพียงโมดูลหลักของโหนด, แบบอะซิงโครนัสอย่างเต็มที่, และขนานการยกเลิกการเชื่อมโยงของไฟล์พร้อมกันทั้งหมดในฟังก์ชั่นห้าบรรทัด
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 });
await Promise.all(entries.map(entry => {
let fullPath = path.join(dir, entry.name);
return entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
}));
await rmdir(dir);
};
ในอีกหมายเหตุเจ้าหน้าที่รักษาความปลอดภัยสำหรับการโจมตีเส้นทางผ่านที่ไม่เหมาะสมสำหรับฟังก์ชั่นนี้เพราะ
rm -rf
ในการที่มันจะโต้แย้งและจะช่วยให้ผู้ใช้rm -rf /
ถ้าถาม มันจะเป็นความรับผิดชอบของสคริปต์เพื่อป้องกันไม่ให้rm
โปรแกรมเอง.isDirectory()
เป็นfalse
สำหรับ SYM การเชื่อมโยงและมีการยกเลิกการเชื่อมโยงไม่ recursed เข้าสุดท้าย แต่ไม่ท้ายสุดมีเงื่อนไขการแข่งขันที่หายากที่การเรียกซ้ำอาจเกิดข้อผิดพลาดหากรายการใดรายการหนึ่งถูกยกเลิกการเชื่อมโยงหรือลบออกนอกสคริปต์นี้ในเวลาที่ถูกต้องในขณะที่การเรียกใช้ซ้ำนี้ทำงานอยู่ เนื่องจากสถานการณ์นี้ไม่ปกติในสภาพแวดล้อมส่วนใหญ่จึงอาจถูกมองข้าม อย่างไรก็ตามหากจำเป็น (ในบางกรณีขอบ) ปัญหานี้สามารถบรรเทาได้ด้วยตัวอย่างที่ซับซ้อนกว่านี้เล็กน้อย:
exports.rmdirs = async function rmdirs(dir) {
let entries = await readdir(dir, { withFileTypes: true });
let results = await Promise.all(entries.map(entry => {
let fullPath = path.join(dir, entry.name);
let task = entry.isDirectory() ? rmdirs(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;
});
await rmdir(dir);
};
แก้ไข:สร้างisDirectory()
ฟังก์ชั่น ลบไดเรกทอรีที่แท้จริงในตอนท้าย แก้ไขการเรียกซ้ำที่ขาดหายไป
await
ตัวคุณPromise.all(…)
; มันตั้งใจหรือไม่ ดูเหมือนว่าในสถานะปัจจุบันresults.forEach
จะย้ำมากกว่าสัญญาในขณะที่รหัสคาดว่าจะซ้ำมากกว่าผลลัพธ์ ฉันพลาดอะไรไปรึเปล่า?
if (!fs.existsSync(dir)) return
readdir
จะโยนข้อผิดพลาดตามที่ควรจะเป็น หากคุณrmdir non-existing-dir
รหัสทางออกเป็นข้อผิดพลาด มันจะเป็นความรับผิดชอบของผู้บริโภคที่จะลอง / จับ นี่เป็นวิธีการเดียวกับที่อธิบายไว้ในเอกสารโหนดเมื่อมันมาถึงการใช้ฟังก์ชั่น fs พวกเขาคาดหวังให้คุณลอง / จับและดูข้อผิดพลาดcode
เพื่อพิจารณาว่าจะทำอย่างไร ตรวจสอบพิเศษแนะนำสภาพการแข่งขัน
fs.exists
ส ป.ล. นี้เป็นทางออกที่ดี
นี่คือคำตอบ @ SharpCoder ของ async เวอร์ชัน
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);
});
});
});
};
ฉันเขียนฟังก์ชันนี้เรียกว่าลบโฟลเดอร์ มันจะลบไฟล์และโฟลเดอร์ทั้งหมดซ้ำ ๆ ในตำแหน่งที่ตั้ง แพ็คเกจเดียวที่ต้องใช้คือ 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)
})
})
})
}
หากคุณกำลังใช้โหนด 8+ ต้องการ asyncronicity และไม่ต้องการการพึ่งพาจากภายนอกนี่คือเวอร์ชัน 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);
}
}
คำตอบ @ SharpCoderเวอร์ชัน Async โดยใช้ fs.promises:
const fs = require('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);
}
};
ฉันมาถึงที่นี่ในขณะที่พยายามเอาชนะด้วยgulp
และฉันกำลังเขียนเพื่อไปให้ถึงต่อไป
gulp-clean
เลิกใช้แล้วสำหรับ gulp-rimraf
gulp-rimraf
เลิกใช้ในความโปรดปรานของ delete-files-folders
เมื่อคุณต้องการลบไฟล์และโฟลเดอร์ที่ใช้del
คุณควรผนวก/**
การลบแบบเรียกซ้ำ
gulp.task('clean', function () {
return del(['some/path/to/delete/**']);
});
แพ็คเกจพฤตินัยคือrimraf
, แต่นี่เป็น async เล็ก ๆ ของฉัน:
const fs = require('fs')
const path = require('path')
const Q = require('q')
function rmdir (dir) {
return Q.nfcall(fs.access, dir, fs.constants.W_OK)
.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()))
})
.then(() => Q.nfcall(fs.rmdir, dir))
}
ในรุ่นล่าสุดของ Node.js (12.10.0 หรือหลังจากนั้น) ซึ่งเป็นrmdir
ฟังก์ชั่นสไตล์fs.rmdir()
, fs.rmdirSync()
และfs.promises.rmdir()
มีตัวเลือกการทดลองใหม่recursive
ที่ช่วยให้การลบไดเรกทอรีที่ไม่ว่างเปล่าเช่น
fs.rmdir(path, { recursive: true });
PR ที่เกี่ยวข้องบน GitHub: https://github.com/nodejs/node/pull/29168
อ้างอิงถึงfs
เอกสาร , fsPromises
ขณะนี้มีrecursive
ตัวเลือกบนพื้นฐานการทดลองซึ่งอย่างน้อยในกรณีของตัวเองบน Windows, เอาไดเรกทอรีและไฟล์ใด ๆ นั้น
fsPromises.rmdir(path, {
recursive: true
})
ไม่recursive: true
ลบไฟล์บน Linux และ MacOS?
ความเร็วสูงพิเศษและป้องกันข้อผิดพลาด
คุณสามารถใช้lignator
แพคเกจ ( https://www.npmjs.com/package/lignator ) มันเร็วกว่ารหัส async ใด ๆ (เช่น rimraf) และข้อผิดพลาดอื่น ๆ อีกมากมาย (โดยเฉพาะอย่างยิ่งใน Windows ซึ่งการลบไฟล์ไม่ได้เกิดขึ้นทันทีและไฟล์อาจ ถูกล็อคโดยกระบวนการอื่น ๆ )
ข้อมูล 4,36 GB, 28,042 ไฟล์, 4 217 โฟลเดอร์ใน Windows ถูกลบใน15 วินาทีเทียบกับ rimraf ของ 60 วินาทีใน HDD เก่า
const lignator = require('lignator');
lignator.remove('./build/');
ซิงค์โฟลเดอร์ลบด้วยไฟล์หรือไฟล์เท่านั้น
ฉันไม่ได้เป็นผู้ให้หรือผู้สนับสนุน แต่ฉันไม่สามารถหาทางออกที่ดีของปัญหานี้ได้และฉันต้องหาวิธีของฉัน ... ดังนั้นฉันหวังว่าคุณจะชอบ :)
การทำงานที่สมบูรณ์แบบสำหรับฉันด้วยจำนวนใด ๆ ไดเร็กทอรีที่ซ้อนกันและไดเร็กทอรีย่อย ข้อควรระวังสำหรับขอบเขตของ '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);
}
ในขณะที่recursive
เป็นตัวเลือกการทดลองของfs.rmdir
function rm (path, cb) {
fs.stat(path, function (err, stats) {
if (err)
return cb(err);
if (stats.isFile())
return fs.unlink(path, cb);
fs.rmdir(path, function (err) {
if (!err || err && err.code != 'ENOTEMPTY')
return cb(err);
fs.readdir(path, function (err, files) {
if (err)
return cb(err);
let next = i => i == files.length ?
rm(path, cb) :
rm(path + '/' + files[i], err => err ? cb(err) : next(i + 1));
next(0);
});
});
});
}
2563 ปรับปรุง
จากเวอร์ชัน 12.10.0 recursiveOptionมีการเพิ่มสำหรับตัวเลือก
โปรดทราบว่าการลบแบบเรียกซ้ำเป็นการทดลองทดลอง
ดังนั้นคุณจะทำการซิงค์:
fs.rmdirSync(dir, {recursive: true});
หรือสำหรับ async:
fs.rmdir(dir, {recursive: true});
เพียงแค่ใช้โมดูล rmdir ! มันง่ายและเรียบง่าย
อีกทางเลือกหนึ่งคือการใช้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+)
วิธีที่รวดเร็วและสกปรก(อาจใช้สำหรับการทดสอบ)อาจใช้วิธีexec
หรือspawn
เรียกการเรียก OS โดยตรงเพื่อลบไดเรกทอรี อ่านเพิ่มเติมเกี่ยวกับNodeJs child_process
let exec = require('child_process').exec
exec('rm -Rf /tmp/*.zip', callback)
ข้อเสียคือ:
ประโยชน์ที่ได้รับ:
-f
ตั้งค่าสถานะเพื่อความปลอดภัยหรือตรวจสอบให้แน่ใจในขณะที่พิมพ์ว่าเขา / เธอจะไม่ลบทุกอย่าง exec + rm
เป็นคำสั่งที่ถูกต้องและมีประโยชน์ในโหนดซึ่งฉันใช้บ่อยในระหว่างการทดสอบ
ฉันหวังว่าจะมีวิธีการทำเช่นนี้โดยไม่มีโมดูลเพิ่มเติมสำหรับบางสิ่งบางอย่างที่เล็กและธรรมดา แต่นี่เป็นสิ่งที่ดีที่สุดที่ฉันสามารถทำได้
อัปเดต: ตอนนี้ควรทำงานบน 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);
});
}
child_process.execFile
ซึ่งไม่ได้เรียกใช้เชลล์และส่งผ่านอาร์กิวเมนต์แทนอย่างชัดเจน
นี่เป็นวิธีการหนึ่งที่ใช้ promisify และฟังก์ชันความช่วยเหลือสองวิธี (ไปยังและทั้งหมด) เพื่อแก้ไขสัญญา
มันทำทุกอย่างแบบอะซิงโครนัส
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)
}
};
// โดยไม่ใช้ lib อื่น ๆ
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);
fs.unllinkSync(path.join(FOLDER_PATH, element);
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!')
}
}
fs.readdir(dirPath)
สำหรับอาร์เรย์ของพา ธ ในโฟลเดอร์ให้ทำซ้ำfs.unlink(filename)
เพื่อลบแต่ละไฟล์จากนั้นจึงfs.rmdir(dirPath)
ลบโฟลเดอร์ว่างทันที หากคุณจำเป็นต้อง recursefs.lstat(filename).isDirectory()
ตรวจสอบ