Shell – Find files with similar names, delete the oldest, rename the most recent

filesrenameshell

I have this situation where there's a lot of files with similar names (but they all follow a pattern) in different subfolders

file1
file1 (Copy)
/folder1/file2.txt
/folder1/file2 (Copy).txt
/folder1/file3.png
/folder1/file3 (Copy).png

Each file is in the same folder of its copy and has the same extension, the difference is that it has (Copy) at the end of the name

I want to get all these files and delete the oldest one, then eventually rename the file from, for example, file1 (Copy) to file1 (that is, remove the (Copy) suffix) if it needs to be renamed.

I was thinking of using find and mv but I'm not sure how to tell it to move the most recent one.

Best Answer

Extended find + bash solution (also needs the GNU implementation of stat):

find . -type f -name "* (Copy).*" -exec bash -c 'p="${0%/*}"; bn="${0##*/}"; 
        main_bn="${bn/ (Copy)/}"; 
        if [ -f "$p/$main_bn" ]; then 
           t_copy_file=$(stat -c %Y "$0"); t_main_file=$(stat -c %Y "$p/$main_bn"); 
           if [[ $t_copy_file -gt $t_main_file ]]; then 
               mv "$0" "$p/$main_bn"; 
           else
               rm "$0"; 
           fi; 
        fi' {} \;

  • p="${0%/*}" - filepath/path with basename trimmed
  • bn="${0##*/}" - file's basename
  • main_bn="${bn/ (Copy)/}" - remove (Copy) substring from the basename to obtain the main/common basename
  • if [ -f "$p/$main_bn" ] - if the main/original file exists (and is found to be a regular file after symlink resolution)
    • t_copy_file=$(stat -c %Y "$0") - get last modification time of found "copy" file
    • t_main_file=$(stat -c %Y "$p/$main_bn") - get last modification time of original file
    • if [[ $t_copy_file -gt $t_main_file ]] - if the "copy" file is the recent one - move it to the original one (make it original) with mv "$0" "$p/$main_bn"
    • otherwise - the original file is the recent one, removing "copy" file with rm "$0"

Or a bit shorter with -nt file test operator ([ new­er­file –nt olderfile ] - check if newerfile was changed more recently than olderfile, or if newerfile exists and olderfile doesn't):

find . -type f -name "* (Copy).*" -exec bash -c 'p="${0%/*}"; bn="${0##*/}"; 
        main_bn="${bn/ (Copy)/}"; 
        if [ -f "$p/$main_bn" ]; then 
           if [ "$0" -nt "$p/$main_bn" ]; then 
               mv "$0" "$p/$main_bn"; 
           else
               rm "$0"; 
           fi; 
        fi' {} \;
Related Question