delete all but the last match

Viewed 72

I want to delete all but the last match of a set of files matching file* that are present in each folder within a directory.

For example:

Folder 1
    file
    file_1-1
    file_1-2
    file_2-1
    stuff.txt
    stuff
Folder 2
    file_1-1
    file_1-2
    file_1-3
    file_2-1
    file_2-2
    stuff.txt
Folder 3
    ...

and so on. Within every subfolder I want to keep only the last of the matched files, so for Folder 1 this would be file_2-1, in Folder 2 it would be file_2-2. The number of files is generally different within each subfolder.

Since I have a very nestled folder structure I thought about using the find command somehow like this

find . -type f -name "file*" -delete_all_but_last_match

I know how to delete all matches but not how to exclude the last match.

I also found the following piece of code:

https://askubuntu.com/questions/1139051/how-to-delete-all-but-x-last-items-from-find

but when I apply a modified version to a test folder

find . -type f -name "file*" -print0 | head -zn-1 | xargs -0 rm -rf

it deletes all the matches in most cases, only in some the last file is spared. So it does not work for me, presumably because of the different number of files in each folder.

Edit:

The folders contain no further subfolders, but they are generally at the end of several subfolder levels. It would therefore be a benefit if the script can be executed some levels above as well.

2 Answers
#!/bin/bash
shopt -s globstar
for dir in **/; do 
    files=("$dir"file*)
    unset 'files[-1]'
    rm "${files[@]}"
done

Try the following solution utilising awk and xargs:

 find . -type f -name "file*" | awk -F/ '{ map1[$(NF-1)]++;map[$(NF-1)][map1[$(NF-1)]]=$0 }END { for ( i in map ) { for (j=1;j<=(map1[i]-1);j++) { print "\""map[i][j]"\"" } } }' | xargs rm

Explanation:

 find . -type f -name "file*" | awk -F/ '{                               # Set the field delimiter to "/" in awk
      map1[$(NF-1)]++;                                     # Create an array map1 with the sub-directory as the index and an incrementing counter the value (number of files in each sub-directory)
      map[$(NF-1)][map1[$(NF-1)]]=$0                       # Create a two dimentional array with the sub directory index one and the file count the second. The line the value
   }
END { 
      for ( i in map ) { 
        for (j=1;j<=(map1[i]-1);j++) { 
           print "\""map[i][j]"\""                         # Loop through the map array utilising map1 to get the last but one file and printing the results
        } 
      } 
    }' | xargs rm                                     # Run the result through xargs rm

Remove the pipe to xargs to verify that the files are listing as expected before adding back in to actually remove the files.

Related