Question

I have a range of files in the format namen.txt and I'd like to remove all but 3 whos n immediately precedes a selected file.

ie. if I input file24.txt I want to delete any file lower than file20.txt

The following works, but is there a simpler way? perhaps using find -name -delete or similar?

file=file24.txt

num=$(sed 's/[^0-9]//g' <<< $file)
((num-=3))
while :
do
  files=($(find $dir -name "*txt"))
  count=${#files[@]}
  if ((count < 1 ))
  then 
   break
  fi
  rm file"$num".txt
  ((num--))
done
Was it helpful?

Solution

Here is one way of doing it:

#!/bin/bash

# Grab the number from file passed to the script. $1 holds the that value
num="${1//[!0-9]}"

# To prevent from matching no files enable this shell option
shopt -s nullglob

# Iterate over the desired path where files are
for file in *; do

# Capture the number from file in loop
    n=${file//[!0-9]}     

# If file has no number and matches your criteria, delete the file
    [[ ! -z $n ]] && (( n < num - 3)) && rm "$file"   
done

Run it as:

./script.sh file24.txt

OTHER TIPS

I'd probably do this with a Perl script:

#!/usr/bin/env perl
use strict;
use warnings;

for my $arg (@ARGV)
{
    my($prefix, $number, $suffix) = ($arg =~ m/^ (\D+) (\d+) (\D.*) $/x);
    foreach my $i (1..$number-4)
    {
        my $file = "$prefix$i$suffix";
        unlink $file;
        print "$file\n";
    }
}

For each of the arguments specified on the command line, the name is split into 3 bits: a non-empty prefix of non-digits, a non-empty number of digits, and a suffix consisting of a non-digit followed by any sequence of characters (so file1.bz2 is split into file, 1 and .bz2). Then, for each number from 1 to 4 less than the given number, generate a file name from the prefix, the current number, and the suffix. With that file name, unlink the file and print the name. You can tweak it to remove only files that exist, or not report the names, or whatever. There's no fixed limit on the maximum number of files.

You could omit the unlink and simply print the file names and send those to xargs rm -f or an equivalent. You could ensure that the names were terminated with a null byte so that names with newlines could be handled correctly by GNU xargs and the -0 option. Etc.

You could code this in pure Bash if you wished to, though the splitting into prefix, number, suffix will be messier than in Perl. I wouldn't use awk for this, though it could probably be forced to do the job if you chose to make it do so.

I think this might be one of the easiest ways of doing it:

shopt -s extglob
rm !(file21.txt|file22.txt|file23.txt)

Here's a simple function that does this in a more generic way:

function rmbut3() {
    for ((n=0 ; n < $(($1 - 3)) ; n++))
    do
        rm file${n}.txt
    done
}

rmbut3 24 # deletes files up to file20.txt
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top