[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bash script last 4 folder delete



On Tue, Oct 12, 2021 at 05:41:59PM +0300, Gokan Atmaca wrote:
> I have directories created at different times. This last updated
> directory is in the directory with a symbolic link named "last".
> Directories are named from 100 to 110. what i want is to delete all
> directories except the folder named "last" and 4 before it. How can I
> do that?
> 
> Exampe_folder_list:
> 100
> 101
> 102
> 103
> 104
> 105
> 106
> 107
> 108
> 109
> 110
> last -> /root/test1/110

I have a serious problem with this example: it doesn't show what happens
when the number of digits changes.  What if the first (oldest) directory
were number 99 instead of 100?  Would it be named "99", or would it be
named "099"?  When you get to directory number 1000, is there going to
be another change in the number of digits?

Because of this ambiguity, we have to assume the *worst* case, which is
that the number of digits changes over time.  The directory names will
be "99", "1000", and so on.  Not a fixed length.

Why does this matter?  Because when you list the directory names and sort
them by name, you will get a mangled sequence:

unicorn:~$ mkdir /tmp/x && cd "$_"
unicorn:/tmp/x$ mkdir 98 99 100 101 102 103 104
unicorn:/tmp/x$ echo *
100 101 102 103 104 98 99

The names are sorted lexicographically, not numerically.  So, the simplest
approaches are NOT permitted.  They will fail.

What does that leave?  Well, the more complicated approaches, of course.

If we believe Gokan's assertion that the directory names will always be
strictly numeric, or the word "last", then we can use "sort -n" to get
them in sequence.

unicorn:/tmp/x$ printf '%s\n' * | sort -n
98
99
100
101
102
103
104

>From there, it becomes pretty straightforward.  We read these names into
an array, and process all except the last 4.

Here's one way to do that:

unicorn:/tmp/x$ mapfile -t dirs < <(printf '%s\n' * | sort -n)
unicorn:/tmp/x$ n=${#dirs[@]}
unicorn:/tmp/x$ echo "${dirs[@]:0:n-4}"
98 99 100

The dirs array holds all of the names.  We count how many there are, and
then process "all but 4" of them, starting from element 0.  In my example,
there are 7 directory names, so we process 3 of them.

We also want to avoid processing "last", so we probably want a glob other
than * for our input.  We could get fancy and use an extended glob like
!(last) -- or we could simply use [0-9]* to match only the names that
begin with a digit.  In the interest of simplicity, let's do the second
thing.

So, the script might look something like this:


#!/bin/bash
cd /wherever || exit 1
mapfile -t dirs < <(printf '%s\n' [0-9]* | sort -n)
n=${#dirs[@]}
echo rm -rf "${dirs[@]:0:n-4}"


This will *print* a command instead of running it.  If you test it and
see that the command looks sensible, then change "echo rm" to "rm" and
voila.


Reply to: