LINUX - Mvr - Mover - Move Directories Recursively with Overwrite

Missing features: if folder doesnt exist in destination just move it, instead of moving everything individually - would be quicker

Back to topic:
It boggles my mind, that a simple tool as this doesnt exist in linux. I actually had to conjure it up with Perl.
Windows has it, its a simple MOVE! So basically take one folder and move it to another, and overwrite everything (I didnt want to implement not overwrite). So you might say mv -r does that, well good luck with that mv -r in linux doesnt exist - dont believe me, look it up. Well then rsync --delete-source-files, well the problem with that is that it deletes the files at the end of the job, what if the filesystem we are on is running out of space and you need to move each file one by one instead of a mass copy then delete - this was lack of space is okay. My tool does all that. All you need is the following programs on linux: perl, rsync, find, and date. Mainly perl and rsync. And bash or sh to run this on of course.

Here is the script

Oh note, im not responsible if this ruins your system? Ok? Ok.. Lets Continue:


#!/usr/bin/perl -w
# By kossboss- Update: April 30th 2013
# Since linux lacks a move-merge-overwrite-destroy-source-files-in-the-process type of command, here it is

if ($#ARGV != 1 ) {
        print "\nMover\n#####\n\nBy: Kossboss\n\nusage: $0 /src /dest\n\n'moves' all files from source to destination\noverwrites each file and after each copy it deletes the source file\n\nWARNING: note that all content from src will be deleted, so backup first. Also empty folders will remain\n\nExample:\n$0 /etc /sysroot/etc\n\nDO NOT INCLUDE THE SLASH AT THE END, FOLLOW THE EXAMPLES\n\n";
        exit;
}

my $arg1=$ARGV[0];
my $arg2=$ARGV[1];


my $src=$arg1;
my $dst=$arg2;

my $fnum=0;
my $err=0;

print "[" . timenow() . "] STARTING MOVER - SRC: $src, DST: $dst\n";

# The main function - loops through all the directories in source and copies every file out with rsync, deleting the original if successful
process_files ($src);

if (($err == 0) && ($fnum > 0)) {
  my $src_q = "\"$src\"";
  print "* Since There Were No Errors - Lets Remove All Empty Folders In The Source Tree: $src_q\n";
  system("find $src_q -type d -empty -delete > /dev/null 2> /dev/null");
}

print "[" . timenow() . "] MOVER FINISHED - $fnum FILES - ERRORS: $err - SRC: $src, DST: $dst\n";

#===============================================
# END OF MAIN LOOP BELOW ARE JUST THE FUNCTION
#=================================================

# Outputs time now, substr removes the last new line character

sub timenow {
   return substr(`date`, 0, -1);
}

# Accepts one argument: the full path to a directory.
# Returns: A list of files that reside in that path.


sub process_files {
    my $path = shift;

    opendir (DIR, $path) or die "Unable to open $path: $!";

    # We are just chaining the grep and map from
    # the previous example.
    # You'll see this often, so pay attention ;)
    # This is the same as:
    # LIST = map(EXP, grep(EXP, readdir()))
    my @files =
        # Third: Prepend the full path
        map { $path . "/" .  $_ }
        # Second: take out '.' and '..'
        grep { !/^\.{1,2}$/ }
        # First: get all files
        readdir (DIR);

    closedir (DIR);

    # my new variables
    my $new_dest;
    my $lensrc = length( $src );
    my $dst1;
    my $src1;
    my $command;
    my $dst_path_slash;
    my $dst_path;
    my $dst_path_q;
    my $attempts1=0;
    # end of my new variables - signed Kossboss

    for (@files) {
        if (-d $_) {
            # Add all of the new files from this directory
            # (and its subdirectories, and so on... if any)
            #  push @files, process_files ($_);
            process_files ($_);

        } else {
            # increment the filenumber
            $fnum = $fnum + 1;
            # the dest full path
            $new_dst = $dst . substr( $_ , $lensrc  );

            # just the directory part
            $dst_path_slash = rindex($new_dst,"/");
            $dst_path = substr($new_dst,0,$dst_path_slash);

            # surround it with quotes so easier to work with
            $dst_path_q = "\"$dst_path\"";

            # print "\n\nPATH NAME: $dst_path_q\n\n";

            # source and destination full path name in quotes
            $dst1="\"$new_dst\"";
            $src1="\"$_\"";

            # the rsync command construction
            $command="rsync -a --remove-source-files $src1 $dst1 > /dev/null 2> /dev/null";

            # print output
            print "$fnum :: $src1 -> $dst1";

            # reset the tried command counter to 0, this is for error circumventing if path not made
            $attempts1 = 0;
            AGAIN:
            my $status = system($command);

            # if there is an error do the top part, if no error print ok

            if (($status >>=8) != 0) {

            # in case of true, meaning there is an error, status code is not 0
            #  mkdir first to insure missing dir is not problem, and try again only once

                if ($attempts1 == 0){
                   # make the path if havent tried making that and redo the command
                   # if it fails again this part of the if wont happen
                   # print "\n\nMAKING DIR: $dst_path_q\n\n";
                   system("mkdir -p $dst_path_q > /dev/null 2> /dev/null");
                   $attempts1=$attempts1+1;
                   goto AGAIN;
                } else {
                   # if already tried making that and have error then its an error
                   $err = $err + 1;
                   print " - ERROR #" .  $err . " @ " . timenow() . " - RSYNC CODE #" . ($status) ;
                }

            }  else {
               print " - OK!"
            }
            print "\n"
        }
    }
    return 0;
}

Comments