#!/bin/ksh93 # @(#)expire 1.4 2005/04/22 # expire - arrange for files to be expired at some later date # # for info, see summary and usages messages below, or run "expire -h" # # Copyright (c) 2005 by Daniel E. Singer. All rights reserved. # Permission is granted to reproduce and distribute this program # with the following conditions: # 1) This copyright notice and the author identification below # must remain in the program and in any copies. # 2) Any modifications to the program must be clearly identified # in the source file. # # Written by Daniel E. Singer, Duke Univ. Dept. of Computer Science, June 2004 # # For updates, please see: # http://www.cs.duke.edu/~des/scripts/ # ftp://ftp.cs.duke.edu/pub/des/scripts/ # # Contact: # Daniel Singer prog=${0##*/} # program basename default_expire_dir="${EXPIRE_EXPIRE_DIR:-$HOME/expire.d}" default_expired_dir="${EXPIRE_TRASH_DIR:-trash.d}" default_expire_arg="${EXPIRE_EXPIRE_DATE:-6m}" summary1=" $prog: This program is used to assign expiration dates to particular files, and then move or remove the files at the appropriate times. Each action is performed manually, but the expirations (removals) can be automated via a cron job. A directory of symbolic links provides the data storage for this system. By default, expired files are moved to a trash directory. " usage=" Usage: $prog [-s] [-qa] [-t time] [-d dir] file... $prog [-s] [-q] -n[a] [-d dir] file... $prog [-s] [-q] -u [-d dir] file... $prog -l [-q] [-d dir] $prog -c [-q] [-d dir] $prog -e [-qrR] [-p [-T date]] [-d dir] $prog -x [-q] [-d dir] file... $prog [-h] (help) " summary2=" Actions: -s set: schedule expirations, default when file args are given; (creates symbolic links); -l list: display entries in the expiration directory; -c check: verify that the scheduled expirations are valid; -e expire: move or remove files that have expired, then the links (archive the files and links to a subdir); -x cross-check: determine which of the files do or don't have expiration links; -h help: display this message, default if no other action selected; Modifiers: -q quiet: fewer non-critical messages; -t time: either a date (yymmdd) or an offset (a number followed by \"d\" (days), \"w\" (weeks), \"m\" (months), or \"y\" (years), default is \"$default_expire_arg\"; -n never: set as never expires; -a alter: re-set expirations with alternate date; -u unset: un-set expirations; -d alternative directory, default is \"$default_expire_dir\"; -r remove: do not backup (move) the expired file to \"$default_expire_dir/$default_expired_dir\"; -R remove: do not backup (move) the expired file or the link to \"$default_expire_dir/$default_expired_dir\"; -p preview: show what would be expired, w/o actually doing it; -T test time: base preview on \"date\" (yymmdd) instead of today; Operands: file relative or full path to a file, directory, etc. Symbolic links to files - and other filesystem objects - to be removed at some future date are maintained in a directory; this program creates and verifies these links, and can also remove the objects when they have expired; the link name format is: link name format: yymmdd.serial expired link name format: yymmdd.serial.yymmddhhmmss moved file name format: yymmdd.serial.yymmddhhmmss. First date is the scheduled date; second is actual removal timestamp; \"serial\" is an incremented, unique serial number. When an object is expired, the link is archived in a subdirectory using the second format. If the object is saved (moved) instead of removed, it is named similarly to the link, with the addition of a \".\" at the end. If the expired target is a directory, it and its contents will be either moved or removed recursively! Note that the symbolic link names and targets within the expire directory function as data pairs within a database. A recommendation for usage of this program: Use the command manually with \"-s\" to schedule (set) expirations; use it with \"-e\" in a 'cron' job to periodically remove (expire) the targets on or after their scheduled dates. Expired files will be moved to \"$default_expire_dir/$default_expired_dir\", unless one of \"-r\" or \"-R\" is selected. These environment variables can be used: EXPIRE_EXPIRE_DATE - default expiration date or offset EXPIRE_EXPIRE_DIR - default expiration directory, should be a full path EXPIRE_TRASH_DIR - default archive subdir of expiration directory " # need some env variables: # - expire dir # - default date offset # - remove/trash options expire_dir="$default_expire_dir" expired_dir="$default_expired_dir" expire_arg="$default_expire_arg" expire_never='999999' serial_file='serial' serial_init_num='1' serial_num_digits='6' serial_lock_file='serial_lock' expire_date= expire_offset= # this is to get around SCCS keyword substitution issues time_fmt_str='%H %M %S' time_fmt_str="${time_fmt_str// /}" # remove spaces date_fmt_str='%y%m%d' # command line flags aflag= cflag= dflag= eflag= hflag= lflag= nflag= pflag= qflag= rflag= Rflag= sflag= tflag= Tflag= uflag= xflag= # some references for readability typeset -n check_links=cflag typeset -n expire_links=eflag typeset -n do_help=hflag typeset -n list_links=lflag typeset -n xcheck_links=xflag typeset -n set_links=sflag typeset -n do_alter=aflag typeset -n do_never=nflag typeset -n do_unset=uflag # # process command line options and arguments # optstr=':cd:aehlnpqrRst:T:ux' opt_error= while getopts "$optstr" option ; do case "$option" in a) aflag=1 ;; c) cflag=1 ;; d) dflag=1 expire_dir="$OPTARG" ;; e) eflag=1 ;; h) hflag=1 ;; l) lflag=1 ;; n) nflag=1 ;; p) pflag=1 ;; q) qflag=1 ;; r) rflag=1 ;; R) Rflag=1 rflag=1 ;; s) sflag=1 ;; t) tflag=1 expire_arg="$OPTARG" ;; T) Tflag=1 debug_date="$OPTARG" ;; u) uflag=1 ;; x) xflag=1 ;; '?') opt_error=1 esac done shift $(( OPTIND - 1 )) function syntax_error { print "\a$prog: option syntax error." >&2 print "$usage" >&2 exit 1 } # messed up options? if [[ $opt_error ]] then syntax_error fi # # look for option mismatches # flagstr="$cflag$eflag$hflag$lflag$sflag" integer num_opts num_opts=${#flagstr} #if (( num_opts > 2 || ( num_opts == 2 && ! ( lflag && eflag ) ) )) then if (( num_opts > 1 )) then syntax_error fi if (( num_opts == 0 )) then if (( $# == 0 )) then hflag=1 else sflag=1 fi fi if [[ $do_help ]] then print "$summary1$usage$summary2" exit 0 fi if [[ ( $tflag || $do_alter || $do_never || $do_unset ) && ! $set_links ]] then syntax_error fi if [[ $tflag && ( $do_never || $do_unset ) ]] then syntax_error fi flagstr="$do_alter$do_never$do_unset" num_opts=${#flagstr} if (( num_opts > 1 )) then [[ $do_unset ]] && syntax_error fi if [[ ( $# != '0' && ! $set_links ) || ( $# == '0' && $set_links ) ]] then print "\a$prog: argument error." >&2 print "$usage" >&2 exit 1 fi if [[ ( $pflag || $rflag || $Rflag ) && ! $expire_links ]] then syntax_error fi if [[ $Tflag ]] then if [[ ! $pflag ]] then syntax_error fi if [[ $debug_date != {6}([0-9]) ]] then print "\a$prog: argument error." >&2 print "$usage" >&2 exit 1 fi fi function leap_year { typeset year year="$1" print $(( ! (year % 4) )) } function days_in_month { typeset year="$1" typeset month="$2" if (( month == 2 )) then print $(( 28 + $(leap_year "$year") )) elif (( month <= 7 )) then print $(( 30 + (month % 2) )) else print $(( 30 + (++month % 2) )) fi } # # get today's date in the format we want to use # function get_date { print -- "$(/bin/date "+$date_fmt_str")" } # # make sure a given date is reasonable # function check_expire_date { typeset base_date="$(get_date)" # accept YYYY, within reason; # if so, truncate it; if (( ${#expire_date} == 8 )) then [[ $expire_date != '20'* ]] && return 1 expire_date="${expire_date#20}" fi # keep stuff from becoming interpreted as octal typeset new_base_date=${base_date##+(0)} typeset new_expire_date=${expire_date##+(0)} # expire date must be beyond today (( new_expire_date <= new_base_date )) && return 1 # make sure there's the right number of digits #expire_date=$( printf "%06d" "$expire_date" ) typeset expire_year="${expire_date:0:2}" typeset expire_month="${expire_date:2:2}" typeset expire_day="${expire_date:4:2}" # keep stuff from becoming interpreted as octal expire_year=${expire_year##+(0)} expire_month=${expire_month##+(0)} expire_day=${expire_day##+(0)} (( expire_month < 1 || expire_month > 12 )) && return 1 (( expire_day < 1 || expire_day > 31 )) && return 1 (( expire_day == 31 && (expire_month <= 7 && !(expire_month % 2)) )) && return 1 (( expire_day == 31 && (expire_month >= 8 && (expire_month % 2)) )) && return 1 (( expire_month == 2 && expire_day > (28 + (expire_year % 4 == 0)) )) && return 1 return 0 } # # compute the date offset # function calc_expire_date { #base_date="$(/bin/date "+$date_fmt_str")" typeset base_date="$(get_date)" typeset base_year="${base_date:0:2}" typeset base_month="${base_date:2:2}" typeset base_day="${base_date:4:2}" # keep stuff from becoming interpreted as octal base_year=${base_year##+(0)} base_month=${base_month##+(0)} base_day=${base_day##+(0)} typeset dnum=${expire_offset%?} case "$expire_offset" in *[yY]) base_year="$(( base_year + dnum ))" ;; *[mM]) base_month="$(( base_month + dnum ))" while (( base_month > 12 )) do (( ++base_year )) (( base_month -= 12 )) done ;; *[wW]|*[dD]) case "$expire_offset" in *[wW]) (( dnum *= 7 )) esac typeset dim base_day="$(( base_day + dnum ))" while (( 1 )) do dim=$(days_in_month $base_year $base_month) (( base_day <= dim )) && break (( base_day -= dim )) (( ++base_month )) if (( base_month > 12 )) then (( ++base_year )) base_month=1 fi done esac expire_date=$(printf "%02d%02d%02d" "$base_year" "$base_month" "$base_day") } #if [[ $tflag ]] then if [[ $set_links && ! $do_unset ]] then if [[ $do_never || $expire_arg == "$expire_never" ]] then expire_date="$expire_never" #if [[ $expire_arg == [1-9]*([0-9])[yYmMwWdD] ]] then elif [[ $expire_arg == [1-9]*([0-9])[yYmMwWdD] ]] then expire_offset="$expire_arg" calc_expire_date if (( ${#expire_date} > 6 )) then print "\a$prog: time offset \"$expire_arg\" too big!" >&2 exit 1 fi elif [[ $expire_arg == {6}([0-9]) || $expire_arg == {8}([0-9]) ]] then expire_date="$expire_arg" check_expire_date || { print "\a$prog: bad date argument \"$expire_arg\"." >&2 exit 1 } else print "\a$prog: bad time argument \"$expire_arg\"." >&2 exit 1 fi #[[ $expire_offset == [1-9]*([0-9])[yYmMdD] ]] || { # print "\a$prog: bad time offset \"$expire_offset\"." >&2 # exit 1 #} #calc_expire_date #if (( ${#expire_date} > 6 )) then # print "\a$prog: time offset \"$expire_offset\" too big!" >&2 # exit 1 # fi fi # # bad dir? # error= if [[ -d $expire_dir ]] then if [[ $set_links || $expire_links ]] then if [[ ! -w $expire_dir ]] then print "\a$prog: \"$expire_dir\" directory is not writable!" >&2 error=1 fi fi else if [[ -e $expire_dir ]] then print "\a$prog: \"$expire_dir\" is not a directory!" >&2 error=1 else if [[ $set_links ]] then if /bin/mkdir "$expire_dir" ; then print "$prog: created \"$expire_dir\" directory." >&2 /bin/mkdir "$expire_dir/$expired_dir" else print "\a$prog: cannot create directory \"$expire_dir\"!" >&2 error=1 fi else print "\a$prog: directory \"$expire_dir\" does not exist!" >&2 error=1 fi fi fi if [[ $error ]] then [[ ! $quiet ]] && print "$prog: aborting." >&2 exit 1 fi # # separate name and target out of "name -> target" from 'ls -l' # function parse_names { name="${1% -> *}" target="${1#* -> }" } # # make list of files already linked to # typeset -A target_list function mk_target_list { typeset line set -f /bin/ls -Al "$expire_dir" | while read line; do #set -- $line [[ $line == 'total '* ]] && continue [[ $line != l* ]] && continue line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)@(\s)} parse_names "$line" (( ++target_list[$target] )) done } # # resolve target to a full path, no symlinks # function resolve_target { typeset target="$1" typeset dir if [[ -d $target ]] then cd "$target" pwd -P cd "$OLDPWD" elif [[ $target == */* ]] then dir=${target%%*(/)*([!/])} target=${target##*/} cd "$dir" print "$(pwd -P)/$target" cd "$OLDPWD" else print "$(pwd -P)/$target" fi } # # bad filenames? (only for set_links); # note that for do_unset, we're seeing if the expire # link exists, not the target file # if [[ $set_links ]] then error= if [[ $do_unset ]] then mk_target_list for file do file=$( resolve_target "$file" ) if [[ ! ${target_list[$file]} ]] then print "\a$prog: no expire link for \"$file\"!" >&2 error=1 fi done else for file do if [[ ! -e $file ]] then print "\a$prog: \"$file\" does not exist!" >&2 error=1 fi done fi if [[ $error ]] then [[ ! $quiet ]] && print "$prog: aborting." >&2 exit 1 fi fi ## ## <-@-> now do the selected action <-@-> ## # # list all contents of expire directory # if [[ $list_links ]] then typeset line set -f [[ ! $qflag ]] && print -- "$prog: contents of \"$expire_dir/\":\n" /bin/ls -AlF "$expire_dir" | while read line; do [[ $line == 'total '* ]] && continue line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)@(\s)} print -r -- "$line" done exit fi # # check entries in expire_dir for correctness, ie, proper naming, # proper targets, etc. # if [[ $check_links ]] then typeset line integer num_checked=0 integer num_checked_bad=0 integer num_checked_warning=0 integer num_checked_skip=0 set -f [[ ! $qflag ]] && print -- "$prog: checking entries in \"$expire_dir/\":\n" mk_target_list /bin/ls -Al "$expire_dir" | while read line; do [[ $line == 'total '* ]] && continue line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)@(\s)} parse_names "$line" # things to skip here: # (should make this into an array at beginning of script) if [[ $name == "$serial_file" || $name == "$expired_dir" || $name == "README" ]] then (( ++num_checked_skip )) continue fi (( ++num_checked )) if [[ ! -L $expire_dir/$name ]] then print -- "$prog: entry \"$line\" is not a symlink!" (( ++num_checked_bad )) continue fi if [[ ! ( $name == {6}([0-9]).+([0-9]) ) ]] then print -- "$prog: entry \"$name\": bad name format!" (( ++num_checked_bad )) fi if [[ ! -e $target ]] then print -- "$prog: for entry \"$line\", the target does not exist!" (( ++num_checked_bad )) elif [[ -L $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a symbolic link!" (( ++num_checked_warning )) elif [[ ! -f $target ]] then if [[ -d $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a directory!" (( ++num_checked_warning )) elif [[ -b $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a block special file!" (( ++num_checked_warning )) elif [[ -c $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a character special file!" (( ++num_checked_warning )) elif [[ -p $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a fifo special file!" (( ++num_checked_warning )) elif [[ -S $target ]] then print -- "$prog: for entry \"$name\", target \"$target\" is a socket!" (( ++num_checked_warning )) fi fi if (( ${target_list[$target]} > 1 )) then print "\a$prog: multiple entries for the target in \"$line\"!" (( ++num_checked_warning )) fi done if [[ ! $qflag ]] then print "\n$prog: $num_checked checked, $num_checked_skip skipped, $num_checked_bad failed, $num_checked_warning warned." fi exit fi # # make list of files already linked to, with link timestamp # typeset -A target_list2 function mk_target_list2 { typeset line set -f /bin/ls -Al "$expire_dir" | while read line; do #set -- $line [[ $line == 'total '* ]] && continue [[ $line != l* ]] && continue line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)@(\s)} parse_names "$line" #(( ++target_list[$target] )) target_list2[$target]="$name" done } # # cross-check: determine which of the files do or don't have # expiration links, and report results; # if [[ $xcheck_links ]] then typeset item typeset linkname integer num_checked=0 integer num_found=0 set -f [[ ! $qflag ]] && print -- "$prog: cross-checking items with \"$expire_dir/\":\n" mk_target_list2 for item do (( ++num_checked )) item=$( resolve_target "$item" ) linkname=${target_list2[$item]} if [[ $linkname ]] then (( ++num_found )) else linkname='-' fi printf "%-13s %s\n" "$linkname" "$item" done if [[ ! $qflag ]] then printf "\n$prog: %d items, %d with expire links, %d without expire links.\n" $num_checked $num_found $(( num_checked - num_found )) fi exit fi function mk_lock { /bin/ln -s "pid=$$ time=$(/bin/date "+$date_fmt_str$time_fmt_str") user=$USER" "$expire_dir/$serial_lock_file" } function rm_lock { rm "$expire_dir/$serial_lock_file" } # # read AND increment the serial number # function get_serial { if [[ ! -e "$expire_dir/$serial_file" ]] then if ! printf "%0*d" "$serial_num_digits" "$serial_init_num" > "$expire_dir/$serial_file" ; then print "\a$prog: cannot create serial file \"$expire_dir/$serial_file\"!" >&2 exit 1 fi fi typeset num typeset new_num read num < "$expire_dir/$serial_file" (( num > 0 )) || { print "\a$prog: cannot read serial file \"$expire_dir/$serial_file\"!" >&2 exit 1 } new_num=${num##+(0)} new_num=$(( new_num + 1 )) if ! printf "%0*d" "$serial_num_digits" "$new_num" > "$expire_dir/$serial_file" ; then print "\a$prog: cannot rewrite serial file \"$expire_dir/$serial_file\"!" >&2 exit 1 fi print "$num" } # # set (create) links indicating files to expire at or after some date; # modifiers: # -a alter (re-set); w/ -n, alter to never # -n set to never # -u un-set # if [[ $set_links ]] then if [[ $do_alter ]] then # alter: find each link, change the link typeset linkname typeset linkserial typeset linkfile integer num_checked=0 integer num_found=0 integer num_altered=0 integer num_failed=0 set -f mk_target_list2 for target do (( ++num_checked )) target=$( resolve_target "$target" ) linkname=${target_list2[$target]} if [[ $linkname ]] then (( ++num_found )) else print -- "\a$prog: no prior expiration scheduled for: \"$target\" ..." (( ++num_failed )) continue fi linkserial=${linkname##+([0-9]).} #print "linkname=$linkname" #print "linkserial=$linkserial" #continue [[ ! $qflag ]] && print -- "$prog: altering expiration for: \"$target\" ..." linkfile="$expire_dir/$linkname" /bin/rm "$linkfile" || { print "\a$prog: problem removing: \"$line\"!" (( ++num_failed )) continue } linkname="$expire_date.$linkserial" #print "linkname=$linkname" linkfile="$expire_dir/$linkname" /bin/ln -s "$target" "$linkfile" || { print "\a$prog: could not create link \"$linkfile\" for \"$target\"!" (( ++num_failed )) continue } target_list2[$target]="$linkname" (( ++num_altered )) done [[ ! $qflag ]] && { #typeset s='s' #typeset s2='s' #typeset t2= #(( $num_set == 1 )) && s= #(( $num_failed != 0 )) && { #(( $num_failed == 1 )) && s2= #t2=" and $num_failed link$s2 failed" #} print "$prog: $num_checked alter attempts, $num_found found, $num_altered altered, $num_failed failed." } elif [[ $do_unset ]] then # find each link, remove it typeset linkname typeset linkfile integer num_checked=0 integer num_found=0 integer num_unset=0 integer num_failed=0 set -f mk_target_list2 for target do (( ++num_checked )) target=$( resolve_target "$target" ) linkname=${target_list2[$target]} if [[ $linkname ]] then (( ++num_found )) else print -- "\a$prog: no prior expiration scheduled for: \"$target\" ..." (( ++num_failed )) continue fi #print "linkname=$linkname" #continue [[ ! $qflag ]] && print -- "$prog: unsetting expiration for: \"$target\" ..." linkfile="$expire_dir/$linkname" /bin/rm "$linkfile" || { print "\a$prog: problem removing link: \"$linkfile\"!" (( ++num_failed )) continue } target_list2[$target]= (( ++num_unset )) done [[ ! $qflag ]] && { print "$prog: $num_checked unset attempts, $num_found found, $num_unset unset, $num_failed failed." } else integer num_checked=0 integer num_set=0 integer num_failed=0 # # lock the serial file # mk_lock || { print "\a$prog: directory \"$expire_dir\" is locked!" >&2 /bin/ls -l "$expire_dir/$serial_lock_file" exit 1 } mk_target_list # # make the links # for target do (( ++num_checked )) target=$( resolve_target "$target" ) if [[ ${target_list[$target]} ]] then print "\a$prog: error: there is already another link to \"$target\"!" (( ++num_failed )) continue fi serial_num=$(get_serial) outfile="$expire_date.$serial_num" /bin/ln -s "$target" "$expire_dir/$outfile" || { print "\a$prog: could not create link \"$expire_dir/$outfile\" for \"$target\"!" (( ++num_failed )) continue } target_list["$target"]=1 (( ++num_set )) done [[ ! $qflag ]] && { #typeset s='s' #typeset s2='s' #typeset t2= #(( $num_set == 1 )) && s= #(( $num_failed != 0 )) && { # (( $num_failed == 1 )) && s2= # t2=" and $num_failed link$s2 failed" #} #print "$prog: $num_set expire link$s set$t2 in \"$expire_dir\"." print "$prog: $num_checked set attempts, $num_set set, $num_failed failed." } # # unlock the serial file # rm_lock || { print "\a$prog: problem unlocking directory: \"$expire_dir\"!" >&2 /bin/ls -l "$expire_dir/$serial_lock_file" exit 1 } fi exit fi # # separate date and serial out of a link name # function parse_link { ldate="${1%.*}" lserial="${1#*.}" } # # expire (remove) items whose time has come (or passed): # - get today's date # - any entries whose date is <= today's date will be removed # - if pflag (preview), just show what would happen # if [[ $expire_links ]] then integer num_checked=0 integer num_skipped=0 integer num_expired=0 integer num_failed=0 typeset ls_Fflag= typeset today_date typeset today_date_hold typeset line set -f if [[ $Tflag ]] then # only used with pflag (preview) for testing today_date="$debug_date" else today_date="$(get_date)" fi today_date_hold="$today_date" today_date=${today_date##+(0)} expire_timestamp="$(/bin/date "+$date_fmt_str$time_fmt_str")" if [[ $pflag ]] then [[ ! $qflag ]] && print "$prog: expire preview: would remove these items with expiration date older than \"$today_date_hold\":\n" ls_Fflag='F' else [[ ! $qflag ]] && print "$prog: expiring items with expiration date older than \"$today_date_hold\"...\n" fi /bin/ls -Al$ls_Fflag "$expire_dir" | while read line; do [[ $line == 'total '* ]] && continue # silently skip non-links [[ $line != l* ]] && continue line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)+(\s)} line=${line##+(\S)+(\s)+(\S)@(\s)} parse_names "$line" # silently skip incorrect name format [[ ! ( $name == {6}([0-9]).+([0-9]) ) ]] && continue (( ++num_checked )) parse_link "$name" #print "ldate = $ldate lserial = $lserial" # keep stuff from becoming interpreted as octal ldate=${ldate##+(0)} #print "ldate = $ldate lserial = $lserial" (( today_date < ldate )) && { (( ++num_skipped )) continue } (( ++num_expired )) if [[ $pflag ]] then print "$line" continue fi # move or remove the target; # move or remove the link; if [[ $rflag ]] then [[ ! $qflag ]] && print -- "$prog: removing: \"$line\" ..." /bin/rm -r "$target" || { print "\a$prog: problem expiring: \"$line\"!" (( --num_expired )) (( ++num_failed )) continue } else [[ ! $qflag ]] && print -- "$prog: moving: \"$line\" ..." /bin/mv "$target" "$expire_dir/$expired_dir/$name.$expire_timestamp." || { print "\a$prog: problem archiving: \"$line\"!" (( --num_expired )) (( ++num_failed )) continue } fi if [[ $Rflag ]] then /bin/rm "$expire_dir/$name" || { print "\a$prog: problem removing link for: \"$line\"!" (( --num_expired )) (( ++num_failed )) continue } else /bin/mv "$expire_dir/$name" "$expire_dir/$expired_dir/$name.$expire_timestamp" || { print "\a$prog: problem archiving link for: \"$line\"!" (( --num_expired )) (( ++num_failed )) continue } fi done if [[ ! $qflag ]] then #typeset s='s' #(( $num_expired == 1 )) && s= if [[ $pflag ]] then #print "$prog: $num_expired item$s to expire." print "\n$prog: $num_expired items to expire." else #print "$prog: $num_expired item$s expired." print "\n$prog: $num_checked entries checked, $num_skipped skipped, $num_expired expired, $num_failed failed." fi #if (( num_failed )) then # typeset s='s' # (( $num_failed == 1 )) && s= # print "$prog: $num_failed item$s failed." # fi fi exit fi ## END ##