Move all files in directory to parent with node.js
Asked Answered
H

4

17

Question

Is there a simple way to move all the files in a directory up to its parent directory then delete the directory?

Use Case

I'm doing a zip extraction and the source zip contains a root folder called archive, so when I extract I get extract_path/archive/, but I'd like to just extract the contents of archive directly to extract_path.

I thought this would be simple rename, but the following is throwing a "There is a file in the way" error message.

fs.renameSync(extractPath + "/archive", extractPath)
Hummingbird answered 10/10, 2014 at 3:51 Comment(0)
O
5

use the mv npm module. mv first tries a fs.rename, and if it fails, uses copy then unlink :

mv('source/dir', 'dest/a/b/c/dir', {mkdirp: true}, function(err) {
  // done. it first created all the necessary directories, and then
  // tried fs.rename, then falls back to using ncp to copy the dir
  // to dest and then rimraf to remove the source dir
});

or spawn a child process :

var spawn = require('child_process').spawn,
    mv = spawn('mv', ['/dir1/dir2/*','dir1/']);
Oklahoma answered 10/10, 2014 at 3:59 Comment(4)
Do you if there is a Sync version of this?Latoyalatoye
Use execSync: var spawn = require('child_precess').execSync;Osborne
This answer does not work for moving a child dir's contents up to the parent dir (which the question asked). Running mv('E:\\tmp\\dir\\sub', 'E:\\tmp\\dir', {mkdirp: true}, console.error); fails with Error: EPERM: operation not permitted, rename 'E:\tmp\dir\sub' -> 'E:\tmp\dir'.Abidjan
not working when dst directory not empty. [Error: ENOTEMPTY: directory not empty,]Bidle
A
7

The selected answer does not work:

var mv = require('mv');
var extractPath = 'E:\\tmp\\dir';
mv(extractPath + "\\sub", extractPath, {mkdirp: true}, console.error);

It errors with:

{ Error: EPERM: operation not permitted, rename 'E:\tmp\dir\sub' -> 'E:\tmp\dir'
    at Error (native)
  errno: -4048,
  code: 'EPERM',
  syscall: 'rename',
  path: 'E:\\tmp\\dir\\sub',
  dest: 'E:\\tmp\\dir' }

Use fs-extra instead of mv:

var fs = require('fs-extra');
var extractPath = 'E:\\tmp\\dir';
fs.move(extractPath + "\\sub", extractPath, console.error);

My file structure is like this before the move:

E:\tmp\dir
    > sub
        > doc.txt

And like this after the move:

E:\tmp\dir
    > doc.txt

UPDATE:

While the above works on Windows, on Linux I get the same error even when using fs-extra. The below is a manual fix for this, by individually moving each child of the subdirectory up to the parent. If a child move fails, then it will revert any other successful moves back to the original location in the subdirectory.

var fs = require('fs-extra')
var Promise = require('promise');
var path = require('path');


var promiseAllWait = function(promises) {
    // this is the same as Promise.all(), except that it will wait for all promises to fulfill before rejecting
    var all_promises = [];
    for(var i_promise=0; i_promise < promises.length; i_promise++) {
        all_promises.push(
            promises[i_promise]
            .then(function(res) {
                return { res: res };
            }).catch(function(err) {
                return { err: err };
            })
        );
    }

    return Promise.all(all_promises)
    .then(function(results) {
        return new Promise(function(resolve, reject) {
            var is_failure = false;
            var i_result;
            for(i_result=0; i_result < results.length; i_result++) {
                if (results[i_result].err) {
                    is_failure = true;
                    break;
                } else {
                    results[i_result] = results[i_result].res;
                }
            }

            if (is_failure) {
                reject( results[i_result].err );
            } else {
                resolve(results);
            }
        });
    });
};

var movePromiser = function(from, to, records) {
    return fs.move(from, to)
    .then(function() {
        records.push( {from: from, to: to} );
    });
};

var moveDir = function(from_dir, to_dir) {
    return fs.readdir(from_dir)
    .then(function(children) {
        return fs.ensureDir(to_dir)
        .then(function() {
            var move_promises = [];
            var moved_records = [];
            var child;
            for(var i_child=0; i_child < children.length; i_child++) {
                child = children[i_child];
                move_promises.push(movePromiser(
                    path.join(from_dir, child),
                    path.join(to_dir, child),
                    moved_records
                ));
            }

            return promiseAllWait(move_promises)
            .catch(function(err) {
                var undo_move_promises = [];
                for(var i_moved_record=0; i_moved_record < moved_records.length; i_moved_record++) {
                    undo_move_promises.push( fs.move(moved_records[i_moved_record].to, moved_records[i_moved_record].from) );
                }

                return promiseAllWait(undo_move_promises)
                .then(function() {
                    throw err;
                });
            });
        }).then(function() {
            return fs.rmdir(from_dir);
        });
    });
};
Abidjan answered 12/6, 2017 at 5:28 Comment(0)
O
5

use the mv npm module. mv first tries a fs.rename, and if it fails, uses copy then unlink :

mv('source/dir', 'dest/a/b/c/dir', {mkdirp: true}, function(err) {
  // done. it first created all the necessary directories, and then
  // tried fs.rename, then falls back to using ncp to copy the dir
  // to dest and then rimraf to remove the source dir
});

or spawn a child process :

var spawn = require('child_process').spawn,
    mv = spawn('mv', ['/dir1/dir2/*','dir1/']);
Oklahoma answered 10/10, 2014 at 3:59 Comment(4)
Do you if there is a Sync version of this?Latoyalatoye
Use execSync: var spawn = require('child_precess').execSync;Osborne
This answer does not work for moving a child dir's contents up to the parent dir (which the question asked). Running mv('E:\\tmp\\dir\\sub', 'E:\\tmp\\dir', {mkdirp: true}, console.error); fails with Error: EPERM: operation not permitted, rename 'E:\tmp\dir\sub' -> 'E:\tmp\dir'.Abidjan
not working when dst directory not empty. [Error: ENOTEMPTY: directory not empty,]Bidle
C
2

Non of the answers work for me, I looked deep in mv's code and found my solution:

I move folder/subfolder to folder, so the folder already exists.

mv(oldPath, newPath, {mkdirp: false, clobber: false}, (err) => {
    if (err) {
        throw err;
    }
});

Remember if the filename already exists in parent folder it will overwrite by file inside subfolder.

Chirr answered 15/3, 2021 at 0:57 Comment(0)
P
0

mv package isn't really up to date and maintained anymore.

I would suggest:

async function moveFolder(oldPath, newPath) {
  console.log(`Try moving ${oldPath} to ${newPath}`)
  if (!fs.existsSync(oldPath)) {
    console.log(`${oldPath} does not exist`)
    return
  }
  if (!fs.existsSync(newPath)) {
    console.log(`Moving ${oldPath} to ${newPath}`)
    fs.renameSync(oldPath, newPath)
    return
  }
  const files = fs.readdirSync(oldPath)
  for (var key in files) {
    const file = files[key]
    const oldFilePath = path.join(oldPath, file)
    const newFilePath = path.join(newPath, file)
    console.log(`Moving ${oldFilePath} to ${newFilePath}`)
    fs.renameSync(oldFilePath, newFilePath)
  })
}
Pilotage answered 14/6 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.