1

I referred the following thread How to delete the oldest directory in a given directory? and the accepted solution is perfect. However, I have a requirement to exempt one folder that is the oldest. How I can accommodate this requirement within the solution provided?

0

You can adapt the original solution to read files until it finds one that is not excluded:

while IFS= read -r -d '' line
do
    file="${line#* }"
    if [ "$file" = './my-excluded-directory' ]
    then
        continue
    fi
    [do stuff with $file]
    break
done < <(find . -maxdepth 1 -type d -printf '%T@ %p\0' | sort -z -n)

(-d '' is equivalent to the original, because you can't have NUL characters in strings.)

reading a long list is slow though, and @mosvy is right that you can do the filtering with some rather nasty find syntax:

IFS= read -r -d '' < <(find . -maxdepth 1 -type d \( -name 'my-excluded-directory' -prune -false \) -o -printf '%T@ %p\0' | sort -z -n)
file="${REPLY#* }"
# do something with $file here
3
  • 1
    maybe it's because I haven't had my coffee yet, but couldn't you just filter ./my-excluded-directory with the ! -path or ! -name option of find?
    – mosvy
    Sep 30 '18 at 8:04
  • find . -maxdepth 1 -type d ( -name 'Development_Instance' -prune -false ) -o -printf '%T@ %p\0' , Where I want to find out the oldest directory other than "Development_Instance". The given code fetches the same directory as the oldest. I am going to try the longer code block provided by l0b0 Sep 30 '18 at 10:13
  • I used the longer script that was given by l0b0 & added an exception for lost+found folder as well. Now, I can get the expected result using the script. Sep 30 '18 at 11:09
0

One "sneaky" way of doing it would be to change the timestamp of the excluded directory at the start, and restoring its timestamp at the end:

$ cd "$(mktemp --directory)"
$ echo foo > example.txt
$ modified="$(date --reference=example.txt +%s)" # Get its original modification time
$ touch example.txt # Set the modification time to now
[delete the oldest file]
$ touch --date="@$modified" example.txt # Restore; the "@" denotes a timestamp

Usual caveats apply:

  • This will fail to restore the original timestamp if killed before completing (or in case of power loss, etc.). If it's really critical to restore the timestamp you could use touch --reference="$path" "${path}.timestamp" to save the timestamp to an actual file and restore using touch --reference="${path}.timestamp" "$path".
  • The option names are based on GNU coretools. You may have to use less readable option names to do the same thing on other *nixes.
0

With zsh:

set -o extendedglob # best in ~/.zshrc
rm -rf -- ^that-folder-to-keep(D/Om[1])

With the solution provided at that other Q&A:

IFS= read -r -d '' line < <(
  find . -maxdepth 1 \
    ! -name that-folder-to-keep \
    -type d -printf '%T@ %p\0' 2>/dev/null | sort -z -n) &&
  rm -rf "${line#* }"

If you want to remove the second oldest:

zsh:

rm -rf -- *(D/Om[2])

GNU utilities:

{ IFS= read -r -d '' line &&
  IFS= read -r -d '' line &&; } < <(
  find . -maxdepth 1 \
    -type d -printf '%T@ %p\0' 2>/dev/null | sort -z -n) &&
  rm -rf "${line#* }"
0

No, that solution is not perfect, because it depends on a lot of highly non-portable features (find -printf, sort -z, read -d, etc).

My solution isn't perfect either, as it depends on the -ot operator of test (which tests if a file is older than another); however that seems to be much better supported (*bsd, busybox, solaris, linux).

# usage with_2nd_oldest 'command' files ...
with_2nd_oldest(){
        cmd="$1"; shift
        oz="$1"; shift; oy=$1
        for f; do
                if [ "$f" -ot "$oz" ]; then oy=$oz oz=$f
                elif [ "$f" -ot "$oy" -a "$f" != "$oz" ]; then oy=$f
                fi
        done
        $cmd "$oy"
}
$ with_2nd_oldest 'echo rm -fr --' dir/*/

This is also able to handle duplicate arguments:

$ for i in `seq 1 9`; do mkdir -p $i; sleep 1; done
$ with_2nd_oldest echo 7 7 1 1 3 3 2 2 8 9 5 1
2

For the other older question, where the very oldest file/directory was required, it's even more simple:

with_oldest(){
        cmd="$1"; shift
        oz="$1"; shift
        for f; do test "$f" -ot "$oz" && oz=$f; done
        $cmd "$oz"
}

Both can be trivially adapted to skip arbitrary paths, or to read the list of files from stdin.

0
#!/bin/bash
while IFS= read -r -d '' line
do
    file="${line#* }"
    if [ "$file" = './Development_Instance' ]
    then
        continue
fi

if [ "$file" = './lost+found' ]
then
continue
fi
    echo $file
    break
done < <(find . -maxdepth 1 -type d -printf '%T@ %p\0' | sort -z -n)

That's how I managed. I know it is not the elegant way to do it. It just does the thing for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.