Is there an easier way to copy a folder and all its content without manually doing a sequence of fs.readir
, fs.readfile
, fs.writefile
Since I'm just building a simple Node.js script, I didn't want the users of the script to need to import a bunch of external modules and dependencies, so I put on my thinking cap and did a search for running commands from the Bash shell.
This Node.js code snippet recursively copies a folder called node-webkit.app to a folder called build:
child = exec("cp -r node-webkit.app build", function(error, stdout, stderr) {
sys.print("stdout: " + stdout);
sys.print("stderr: " + stderr);
if(error !== null) {
console.log("exec error: " + error);
} else {
}
});
Thanks to Lance Pollard at dzone for getting me started.
The above snippet is limited to Unix-based platforms, like macOS and Linux, but a similar technique may work for Windows.
For a Linux/Unix OS, you can use the shell syntax
const shell = require('child_process').execSync;
const src = `/path/src`;
const dist = `/path/dist`;
shell(`mkdir -p ${dist}`);
shell(`cp -r ${src}/* ${dist}`);
That's it!
There are some modules that support copying folders with their content. The most popular would be wrench.js:
// Deep-copy an existing directory
wrench.copyDirSyncRecursive('directory_to_copy', 'location_where_copy_should_end_up');
An alternative would be node-fs-extra:
fs.copy('/tmp/mydir', '/tmp/mynewdir', function (err) {
if (err) {
console.error(err);
} else {
console.log("success!");
}
}); // Copies directory, even if it has subdirectories or files
This is how I did it:
let fs = require('fs');
let path = require('path');
Then:
let filePath = // Your file path
let fileList = []
var walkSync = function(filePath, filelist)
{
let files = fs.readdirSync(filePath);
filelist = filelist || [];
files.forEach(function(file)
{
if (fs.statSync(path.join(filePath, file)).isDirectory())
{
filelist = walkSync(path.join(filePath, file), filelist);
}
else
{
filelist.push(path.join(filePath, file));
}
});
// Ignore hidden files
filelist = filelist.filter(item => !(/(^|\/)\.[^\/\.]/g).test(item));
return filelist;
};
Then call the method:
This.walkSync(filePath, fileList)
The one with symbolic link support + doesn't throw if the destination directory already exists.
function copyFolderSync(from, to) {
try {
fs.mkdirSync(to);
} catch(e) {}
fs.readdirSync(from).forEach((element) => {
const stat = fs.lstatSync(path.join(from, element));
if (stat.isFile()) {
fs.copyFileSync(path.join(from, element), path.join(to, element));
} else if (stat.isSymbolicLink()) {
fs.symlinkSync(fs.readlinkSync(path.join(from, element)), path.join(to, element));
} else if (stat.isDirectory()) {
copyFolderSync(path.join(from, element), path.join(to, element));
}
});
}
If you are on Linux, and performance is not an issue, you may use the exec
function from child_process
module, to execute a Bash command:
const { exec } = require('child_process');
exec('cp -r source dest', (error, stdout, stderr) => {...});
In some cases, I found this solution cleaner than downloading an entire module or even using the fs
module.