-1

I have a task to do for which i am able to think of logic but unable to implement in Unix.

I have a directory in which its sub-directories are having there naming pattern as 'mmddyy'. So my task here is to delete all the directories older than X days. The Date-stamp in question is not Unix Timestamp but the Date-stamp as resembeled by directory name.

My Approach:

  1. Get list of dir name in array
  2. If (SYSDATE - array > 30) Then Remove Else Continue.

How do we solve using Unix?

3 Answers 3

1

reformat the date details in the file into yy-mm-dd and then use date and the seconds since epoch

X=30 - 30 days ago

epc=$(date -d -"$X"days +%s)

epc is the reference epoch string to check against

Then process the date in the file name

example date in file name

 dat="010188" 
 dat1=awk '{print substr($0,5,2)"-"substr($0,3,2)"-"substr($0,1,2)}' <<< $dat
 refepc=$(date -d "$dat1" +%s)

refepc is then your reference epoch that can be checked against epc

0
then=$( date -d '30 days ago' +%y%m%d )

for d in *; do
    if [[ -d "$d" ]] && [[ "$d" =~ ^([0-9][0-9])([0-9][0-9])([0-9][0-9])$ ]]; then
        datestring="${BASH_REMATCH[3]}${BASH_REMATCH[1]}${BASH_REMATCH[2]}"

        if [[ "$datestring" < "$then" ]]; then
            echo rm -rf "$d"
        fi
    fi
done

This bash script would first figure out the date of 30 days ago using GNU date and storing it in the YYMMDD format in $then.

It then iterates over the names found in the current directory. If any of the found names is a directory that matches the given regular expression, it picks out the parts of the directory name and arranges them in the YYMMDD format in $datestring.

If $datestring is lexicographically less than $then, then this directory should be deleted. The echo prevents this from actually happening, so run it first a few times to make sure that it works before removing it.

Testing:

$ mkdir {01..12}0117  # one directory for the 1st of every month this year
$ bash ./script.sh
rm -rf 010117
rm -rf 020117
rm -rf 030117
rm -rf 040117
rm -rf 050117
rm -rf 060117
rm -rf 070117
rm -rf 080117

(today's date is 170922 and $then has the value 170823)

This would obviously fail for directory names containing dates in the last century (these would not be deleted).

1
  • @StéphaneChazelas I'll be using < for a lexicographical comparison then. The note about pre-2000 dates is already there. Thanks again!
    – Kusalananda
    Commented Sep 22, 2017 at 14:40
0

With zsh:

#! /usr/bin/env zsh
days=${1-30}

zmodload zsh/datetime || exit

strftime -s cutoff %Y%m%d $((EPOCHSECONDS - days * 24 * 60 * 60))

old_enough() {
  year=$REPLY[5,6]
  ((year += 1900 + (year < 70) * 100))
  [[ $year$REPLY[1,4] < $cutoff ]]
}

set -o extendedglob
echo rm -rf [0-9](#c6)(/+old_enough)

Remove the echo or pipe to sh if happy.

With ksh93, an equivalent could be:

#! /usr/bin/env ksh
days=${1-30}

cutoff=$(printf '%(%Y%m%d)T' "$days days ago")

old_enough() {
  year=${1:4}
  ((year += 1900 + (year < 70) * 100))
  [[ $year${1:0:4} < $cutoff ]]
}
dirs=()
for f in {6}([0-9]); do
  [ -d "$f" ] && [ ! -L "$f" ] && old_enough "$f" && dirs+=("$f")
done
if ((${#dirs[@]})); then
  echo rm -rf "${dirs[@]}"
else
  echo >&2 "No match"
  exit 1
fi

With bash, an equivalent could be:

#! /usr/bin/env bash
days=${1-30}

printf -v now '%(%s)T' -1
printf -v cutoff '%(%Y%m%d)T' "$((now - days * 24 * 60 * 60))"

old_enough() {
  year=${1:4}
  year=${year#0} # work around bug where bash complains about 08 and 09
  ((year += 1900 + (year < 70) * 100))
  [[ $year${1:0:4} < $cutoff ]]
}
shopt -s nullglob
dirs=()
for f in [0-9][0-9][0-9][0-9][0-9][0-9]; do
  [ -d "$f" ] && [ ! -L "$f" ] && old_enough "$f" && dirs+=("$f")
done
if ((${#dirs[@]})); then
  echo rm -rf "${dirs[@]}"
else
  echo >&2 "No match"
  exit 1
fi

Years above 70 are assumed to be from the last century (123199 means 1999-12-31, 092217 means 2017-09-22).

If you have neither of those shells and are on a Unix (not GNU (UNG)) system, then your best bet may be to resort to perl:

#! /usr/bin/env perl
use POSIX;
$cutoff = time - ($ARGV[0] // 30) * 24 * 60 * 60;
$, = " "; $\ = "\n";
if (@dirs = grep {
    /^(\d\d)(\d\d)(\d\d)$/ && ! -l && -d && 
    mktime(0,0,0,$2,$1-1,$3+100*($3<70)) < $cutoff} <*>) {
  print "rm -rf", @dirs;
} else {
  die "No match";
}

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.