432 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			432 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| #
 | |
| # Helper script to rotate contents of a folder with a daily-weekly-monthly plan.
 | |
| # TL;DR: rotate_folder -f path/to/my/folder         # Shows what would be done.
 | |
| #        rotate_folder -f path/to/my/folder --doit  # Makes the job.
 | |
| #
 | |
| # By default the script makes a dry run - doesn't delete anything, only lists
 | |
| # the operations would be done. You may force the execution by --doit command
 | |
| # line parameter. Another optional command line parameter is -f followed by
 | |
| # the pathname of the folder intended to be rotated. Lack of it the script
 | |
| # assumes the current folder (pwd).
 | |
| #
 | |
| # You may configure the script by environment variables and/or by a
 | |
| # configuration textfile. This file should be placed into the folder intended
 | |
| # to be rotated. It's name should be a dot followed by the script's name and a
 | |
| # .conf extension (.rotate_folder.conf by default). The script will create a
 | |
| # default config file automatically on first (dry) run, if it doesn't exist.
 | |
| #
 | |
| # The configurable parameters and their defaults are:
 | |
| # BACKUP_FOLDER=""		# pathname of the folder intended to be rotated
 | |
| # CLASSES_PATTERN=""		# see below
 | |
| # DOIT=""			# if empty the script makes a dry run
 | |
| # RETAIN_DAYS=7			# retains all files created within that many days
 | |
| # RETAIN_WEEKS=4		# retains one file per week/month,
 | |
| # RETAIN_MONTHS=12		# created within that many weeks/months
 | |
| #
 | |
| # If you specify a CLASSES_PATTERN the script will classify the files in folder
 | |
| # and rotate the files class by class independently. A pattern is a regexp:
 | |
| # * the script considers only the filenames matching the whole regexp;
 | |
| # * the regexp must contain parts in capturing parentheses (classifiers).
 | |
| #   A class is a set of filenames where the matching part to the all classifiers
 | |
| #   are the same. For example, if CLASSES_PATTERN='^(.*)-[0-9].tgz'
 | |
| #   then "alpha-1.tgz alpha-2.tgz ... alpha-9.tgz" are members of a class;
 | |
| #   "beta-1.tgz beta-2.tgz ... beta-9.tgz" are members of another class.
 | |
| #   "beta-10.tgz gamma-1.log" won't be processed beacuse they don't match
 | |
| #   the pattern at all.
 | |
| #   In this example the "alpha" and "beta" files will be rotated independently.
 | |
| #
 | |
| # The rotating rules are:
 | |
| # * all files created within RETAIN_DAYS will be retained.
 | |
| # * furthermore from files created within RETAIN_WEEKS, only one file
 | |
| #   (the oldest) will be retained for every 7 days period.
 | |
| # * furthermore from files created within RETAIN_MONTHS, only one file
 | |
| #   (the oldest) will be retained for every 30 days period.
 | |
| #
 | |
| # On dry run the script lists all the files of the class with following
 | |
| # abbreviations:
 | |
| #   DR filename - would be retained by daily   rule
 | |
| #   WR filename - would be retained by weekly  rule
 | |
| #   WX filename - would be deleted  by weekly  rule
 | |
| #   MR filename - would be retained by monthly rule
 | |
| #   MX filename - would be deleted  by monthly rule
 | |
| #   AX filename - would be deleted  no rule match it, because is too old
 | |
| #
 | |
| # Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
 | |
| #         Kovács Zoltán <kovacsz@marcusconsulting.hu>
 | |
| # License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html)
 | |
| # 2023-06-18 v1.0
 | |
| # new: forked from the "SMARTERP_skeleton" repository.
 | |
| # 2021.02.12 v0.3
 | |
| # add: Multiple classes (mainly rewritten).
 | |
| # mod: Accepts the first command line parameter as a folder (doesn't
 | |
| #      need the -f option). But the folder doesn't defaults to the $PWD.
 | |
| # 2020-11-24 v0.2
 | |
| # fix: Typos.
 | |
| # mod: Warnings also go to the STDERR.
 | |
| # 2020-11-02 v0.1 Initial release
 | |
| 
 | |
| # Accepted environment variables and their defaults.
 | |
| #
 | |
| BACKUP_FOLDER=${BACKUP_FOLDER-""}
 | |
| CLASSES_PATTERN=${CLASSES_PATTERN-""}
 | |
| RETAIN_DAYS=${RETAIN_DAYS-"7"}
 | |
| RETAIN_WEEKS=${RETAIN_WEEKS-"4"}
 | |
| RETAIN_MONTHS=${RETAIN_MONTHS-"12"}
 | |
| 
 | |
| # Other initialisations (maybe overridden by configuration).
 | |
| #
 | |
| DOIT=""
 | |
| 
 | |
| # Messages (maybe overriden by configuration).
 | |
| #
 | |
| MSG_BADFOLDER="Doesn't exist or doesn't writable"
 | |
| MSG_BADOPT="Invalid option"
 | |
| MSG_BADPATTERN="The pattern given seems to be illegal"
 | |
| MSG_CREATED="A new, empty configuration has been created.\n"
 | |
| MSG_CREATED+="Feel free to fill in and rerun this program!\n"
 | |
| MSG_CREATED+="You may force the execution unconfigurated with --doit option."
 | |
| MSG_DELDRY="Dry run - these files would have been deleted:"
 | |
| MSG_DELREAL="These files have been deleted:"
 | |
| MSG_FAILCREATE="Failed to create a new, empty configuration file.\n"
 | |
| MSG_FAILCREATE+="You may force the execution unconfigurated with --doit option."
 | |
| MSG_MISSINGDEP="Fatal: missing dependency"
 | |
| MSG_NOCONF="Didn't find the configuration file"
 | |
| MSG_NOCLASSES="Didn't find suitable classes according to pattern"
 | |
| MSG_NOFILES="Didn't found files to rotate."
 | |
| MSG_SCHEDULE="Dry run - this is the schedule:"
 | |
| MSG_TODOIT="Dry run - you may force the execution with --doit option."
 | |
| 
 | |
| # There is nothing to configure below (I hope).
 | |
| ###############################################
 | |
| 
 | |
| # Getting command line options.
 | |
| while getopts ":-:f:" option
 | |
| do
 | |
|     case ${option} in
 | |
|         "-" )
 | |
|             if   [ "$OPTARG" = "doit" ]; then DOIT="yes"
 | |
| 	    else echo "$MSG_BADOPT --$OPTARG" >&2; exit 1
 | |
|             fi
 | |
|         ;;
 | |
| 	"f" ) BACKUP_FOLDER="$OPTARG" ;;
 | |
|         \?  ) echo "$MSG_BADOPT -$OPTARG" >&2; exit 1 ;;
 | |
|     esac
 | |
| done
 | |
| # Done with options.
 | |
| 
 | |
| # Checks the dependencies.
 | |
| TR=$(which tr 2>/dev/null)
 | |
| if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
 | |
| for item in basename date dirname egrep sed seq sort stat xargs
 | |
| do
 | |
|     if [ -n "$(which $item)" ]
 | |
|     then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item)
 | |
|     else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
 | |
| done
 | |
| # All dependencies are available via "$THECOMMAND" (upper case) call.
 | |
| 
 | |
| # Checks the backup folder.
 | |
| # If wasn't defined yet accepts the 1st command line parameter as well.
 | |
| if [ -z "$BACKUP_FOLDER" ]; then BACKUP_FOLDER="$1"; shift; fi
 | |
| # Removes the trailing slash (if any).
 | |
| BACKUP_FOLDER=${BACKUP_FOLDER%/}
 | |
| # Checks and gives up here if fails.
 | |
| if [ -z "$BACKUP_FOLDER" -o ! -d "$BACKUP_FOLDER" -o ! -w "$BACKUP_FOLDER" ]
 | |
| then echo -e "$MSG_BADFOLDER $BACKUP_FOLDER" >&2; exit 1; fi
 | |
| 
 | |
| # Gets the configuration (if any).
 | |
| BACKUP_CONF="$BACKUP_FOLDER/.$("$BASENAME" "$0").conf"
 | |
| if [ -r $BACKUP_CONF ]; then . "$BACKUP_CONF"
 | |
| else
 | |
|     # Warns about failure.
 | |
|     echo -e "$MSG_NOCONF $BACKUP_CONF"
 | |
|     # When on dry run tries to write a new file with some help text and defaults.
 | |
|     if [ -z "$DOIT" -a -z "$CLASSES_PATTERN" ]; then
 | |
| 	cat > "$BACKUP_CONF" 2>/dev/null << EOF
 | |
| # This is a shell script excerpt for configuration purposes only.
 | |
| # Handle with care! Please don't put code here, only variables.
 | |
| # The configurable parameters for $("$BASENAME" "$0") script and their defaults are:
 | |
| 
 | |
| # CLASSES_PATTERN=""		# see below
 | |
| # DOIT=""			# if empty the script makes a dry run
 | |
| # RETAIN_DAYS=7			# retains all files created within that many days
 | |
| # RETAIN_WEEKS=4		# retains one file per week/month,
 | |
| # RETAIN_MONTHS=12		# created within that many weeks/months
 | |
| 
 | |
| # If you specify a CLASSES_PATTERN the script will classify the files in folder
 | |
| # and rotates the files class by class independently. A pattern is a regexp:
 | |
| # * the script considers only the filenames matching the whole regexp;
 | |
| # * the regexp must contain parts in capturing parentheses (classifiers).
 | |
| #   A class is a set of filenames where the matching part to the all classifiers
 | |
| #   is the same. For example, if CLASSES_PATTERN='^(.*)-[0-9].tgz'
 | |
| #   then "alpha-1.tgz alpha-2.tgz ... alpha-9.tgz" are members of a class;
 | |
| #   "beta-1.tgz beta-2.tgz ... beta-9.tgz" are members of another class.
 | |
| #   "beta-10.tgz gamma-1.log" won't be processed beacuse they don't match
 | |
| #   the pattern at all.
 | |
| #   In this example the "alpha" and "beta" files will be rotated independently.
 | |
| #
 | |
| # The rotating rules are:
 | |
| # * all files have created within RETAIN_DAYS will be retained.
 | |
| # * furthermore from files created within RETAIN_WEEKS, only one file
 | |
| #   (the oldest) will be retained for every 7 days period.
 | |
| # * furthermore from files created within RETAIN_MONTHS, only one file
 | |
| #   (the oldest) will be retained for every 30 days period.
 | |
| #
 | |
| # On dry run the script lists all the files of the class with following
 | |
| # abbreviations:
 | |
| #   DR filename - would be retained by daily   rule
 | |
| #   WR filename - would be retained by weekly  rule
 | |
| #   WX filename - would be deleted  by weekly  rule
 | |
| #   MR filename - would be retained by monthly rule
 | |
| #   MX filename - would be deleted  by monthly rule
 | |
| #   AX filename - would be deleted  no rule match it, because is too old
 | |
| EOF
 | |
| 	# Reports the success or failure and stops here.
 | |
| 	if [ -r "$BACKUP_CONF" ];
 | |
| 	then echo -e "$MSG_CREATED" >&2; exit
 | |
| 	else echo -e "$MSG_FAILCREATE" >&2; exit 1; fi
 | |
|     fi
 | |
| fi
 | |
| # Configuration file has been handled.
 | |
| 
 | |
| # Initialisations which are protected from configuration.
 | |
| (( SECS_DAY = 60*60*24 ))
 | |
| (( SECS_WEEK = 7*SECS_DAY ))
 | |
| (( SECS_MONTH = 30*SECS_DAY ))
 | |
| TIMESTAMP=$("$DATE" '+%s')
 | |
| 
 | |
| # This function rotates the files matching to its parameter
 | |
| # which is a definite regexp (without parenthesised parts).
 | |
| function rotate_class {
 | |
| 
 | |
|     local CLASSES_PATTERN="$1"; shift
 | |
|     local files
 | |
|     # Selection of files to rotate.
 | |
|     #
 | |
|     # We consider only the files matching to the pattern.
 | |
|     # If the pattern is empty, we'll consider all files.
 | |
|     if [ -z "$CLASSES_PATTERN" ]; then
 | |
| 	# All non-hidden files but no subfolders, symlinks, etc.
 | |
| 	files=$(cd "$BACKUP_FOLDER"; \
 | |
|     	        ls -1 -t --file-type | "$XARGS" -0 | "$EGREP" -v '[/=>@|$]$' )
 | |
|     else
 | |
| 	# Non-hidden files (but no subfolders, symlinks, etc.) matching to the pattern.
 | |
| 	files=$(cd "$BACKUP_FOLDER"; \
 | |
|     	        ls -1 -t --file-type | "$XARGS" -0 | "$EGREP" "$CLASSES_PATTERN" )
 | |
|     fi
 | |
|     # Lack of files gives it up here.
 | |
|     [[ -z "$files" ]] && return
 | |
|     # Converts the list into an array.
 | |
|     local class_files=($files)
 | |
|     # We need to process the files listed within the class_files array.
 | |
|     # The list is ordered by modification time, reverse.
 | |
|     # We'll start with the youngest and step toward the oldest.
 | |
| 
 | |
|     # Collectcs the list of files to delete within this class.
 | |
|     #
 | |
|     local delete_files=""			# list of filenames to delete
 | |
|     local pointer=0				# class_files index to process
 | |
|     local file_mtime
 | |
|     local file_toretain
 | |
|     local threshold
 | |
|     # Starts with the daily schedule.
 | |
|     # We'll retain all files within this schedule.
 | |
|     [[ -z "$DOIT" ]] && echo -e "$MSG_SCHEDULE"
 | |
|     local last_retained=""
 | |
|     for day in $("$SEQ" 1 "$RETAIN_DAYS")
 | |
|     do
 | |
| 	# Finishes if we've no more files.
 | |
| 	[[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	(( threshold = TIMESTAMP - (day * SECS_DAY) ))
 | |
| 	file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	# We'll retain all files of this day.
 | |
| 	while [[ $file_mtime -ge $threshold ]]
 | |
| 	do
 | |
| 	    [[ -z "$DOIT" ]] && echo "DR ${class_files[$pointer]}"
 | |
| 	    last_retained="$file_mtime"
 | |
| 	    # Next file; finishes if we're no more files.
 | |
| 	    (( pointer++ )); [[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	    file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	done
 | |
| 	# This day concluded.
 | |
|     done
 | |
|     # The daily schedule concluded.
 | |
|     # If we didn't save any file within this schedule we'll retain this file.
 | |
|     if [[ -z "$last_retained" &&  $pointer -lt ${#class_files[@]} ]]; then
 | |
| 	last_retained="$file_mtime"
 | |
| 	[[ -z "$DOIT" ]] && echo "DR ${class_files[$pointer]}"
 | |
|         (( pointer++ ))
 | |
| 	[[ $pointer -lt ${#class_files[@]} ]] \
 | |
| 	&& file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
|     fi
 | |
| 
 | |
|     # The weekly schedule.
 | |
|     # We'll retain only the oldest file from a week within this schedule.
 | |
|     last_retained=""
 | |
|     for week in $("$SEQ" 1 "$RETAIN_WEEKS")
 | |
|     do
 | |
| 	file_toretain=""
 | |
| 	# Finishes if we've no more files.
 | |
| 	[[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	(( threshold = TIMESTAMP - (week * SECS_WEEK) ))
 | |
| 	file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	while [[ $file_mtime -ge $threshold ]]
 | |
| 	do
 | |
| 	    if [ -z "$file_toretain" ]; then
 | |
| 		# This is the first file from this week.
 | |
| 		# marks it to retain temporailly.
 | |
| 		file_toretain="${class_files[$pointer]}"
 | |
| 	    else
 | |
| 		# This is an older file from this week than the previous.
 | |
| 		# Changes the marker, the previous file should be deleted.
 | |
| 		delete_files+="$file_toretain\n"
 | |
| 		[[ -z "$DOIT" ]] && echo "WX $file_toretain"
 | |
| 		file_toretain="${class_files[$pointer]}"
 | |
| 	    fi
 | |
| 	    # Next file; finishes if we're no more files.
 | |
| 	    (( pointer++ )); [[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	    file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	done
 | |
| 	# The marked file from the week passed has been retained.
 | |
| 	if [ -n "$file_toretain" ]; then
 | |
| 	    last_retained=$file_mtime	# a cheat but it isn't important here
 | |
| 	    [[ -z "$DOIT" ]] && echo "WR $file_toretain"
 | |
| 	fi
 | |
| 	# This week concluded.
 | |
|     done
 | |
|     # The weekly schedule concluded.
 | |
|     # If we didn't save any file within this schedule we'll retain this file.
 | |
|     if [[ -z "$last_retained" &&  $pointer -lt ${#class_files[@]} ]]; then
 | |
| 	last_retained="$file_mtime"
 | |
| 	[[ -z "$DOIT" ]] && echo "WR ${class_files[$pointer]}"
 | |
| 	(( pointer++ ))
 | |
| 	[[ $pointer -lt ${#class_files[@]} ]] \
 | |
| 	&& file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
|     fi
 | |
| 
 | |
|     # The monthly schedule.
 | |
|     # We'll retain only the oldest file from a month within this schedule.
 | |
|     last_retained=""
 | |
|     for month in $("$SEQ" 1 "$RETAIN_MONTHS")
 | |
|     do
 | |
| 	file_toretain=""
 | |
| 	# Finishes if we've no more files.
 | |
| 	[[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	(( threshold = TIMESTAMP - (month * SECS_MONTH) ))
 | |
| 	file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	while [[ $file_mtime -ge $threshold ]]
 | |
| 	do
 | |
| 	    if [ -z "$file_toretain" ]; then
 | |
| 		# This is the first file from this month.
 | |
| 		# marks it to retain temporailly.
 | |
| 		file_toretain="${class_files[$pointer]}"
 | |
| 	    else
 | |
| 		# This is an older file from this month than the previous.
 | |
| 		# Changes the marker, the previous file should be deleted.
 | |
| 		delete_files+="$file_toretain\n"
 | |
| 		[[ -z "$DOIT" ]] && echo "MX $file_toretain"
 | |
| 		file_toretain="${class_files[$pointer]}"
 | |
| 	    fi
 | |
| 	    # Next file; finishes if we're no more files.
 | |
| 	    (( pointer++ )); [[ $pointer -ge ${#class_files[@]} ]] && break
 | |
| 	    file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
| 	done
 | |
| 	# The marked file from the month passed has been retained.
 | |
| 	if [ -n "$file_toretain" ]; then
 | |
| 	    last_retained=$file_mtime	# a cheat but it isn't important here
 | |
| 	    [[ -z "$DOIT" ]] && echo "MR $file_toretain"
 | |
| 	fi
 | |
| 	# This month concluded.
 | |
|     done
 | |
|     # The monthly schedule concluded.
 | |
|     # If we didn't save any file within this schedule we'll retain this file.
 | |
|     if [[ -z "$last_retained" &&  $pointer -lt ${#class_files[@]} ]]; then
 | |
| 	last_retained="$file_mtime"
 | |
| 	[[ -z "$DOIT" ]] && echo "MR ${class_files[$pointer]}"
 | |
| 	(( pointer++ ))
 | |
| 	[[ $pointer -lt ${#class_files[@]} ]] \
 | |
| 	&& file_mtime=$("$STAT" -c %Y "$BACKUP_FOLDER/${class_files[$pointer]}")
 | |
|     fi
 | |
| 
 | |
|     # All the schedules have been processed.
 | |
|     # The remaining files will be deleted all.
 | |
|     while [[ $pointer -lt ${#class_files[@]} ]]
 | |
|     do
 | |
| 	delete_files+="${class_files[$pointer]}\n"
 | |
| 	[[ -z "$DOIT" ]] && echo "AX ${class_files[$pointer]}"
 | |
| 	(( pointer ++ ))
 | |
|     done
 | |
| 
 | |
|     # The delete_files contain the list of iles to delete according this class.
 | |
|     if [ -n "$delete_files" ]; then
 | |
|         if [ -z "$DOIT" ]; then
 | |
| 	    # Simulated deletion.
 | |
| 	    echo -e "\n$MSG_DELDRY\n$delete_files"
 | |
| 	else
 | |
| 	    # Actual deletion file by file.
 | |
| 	    for file in $(echo -e "$delete_files")
 | |
| 	    do [[ -n "$file" ]] && rm "$BACKUP_FOLDER/$file" #2>/dev/null
 | |
| 	    done
 | |
| 	    echo -e "\n$MSG_DELREAL\n$delete_files"
 | |
| 	fi
 | |
|     else
 | |
| 	# Uniform output formatting.
 | |
| 	[[ -z "$DOIT" ]] && echo
 | |
|     fi
 | |
| }
 | |
| 
 | |
| # This function parses the given class pattern, recursively explores
 | |
| # the classes, subclasses, sub-subclasses and so on, then calls the
 | |
| # rotator function for each definite class.
 | |
| function rotate_classes {
 | |
| 
 | |
|     local CLASSES_PATTERN="$1"; shift
 | |
|     [[ -z "$CLASSES_PATTERN" ]] && return # unusable
 | |
| 
 | |
|     # Tries to validate the pattern.
 | |
|     # Test calls simulate the later use.
 | |
|     if [ -n "$CLASSES_PATTERN" ]; then
 | |
| 	echo "test" | "$EGREP" "$CLASSES_PATTERN" >/dev/null 2>&1
 | |
| 	[[ $? -gt 1 ]] && return # unusable
 | |
|     fi
 | |
|     # Does contain unexplored classifiers?
 | |
|     echo "test" | "$SED" -E "s/$CLASSES_PATTERN/\1/"  >/dev/null 2>&1
 | |
|     if [[ $? -gt 0 ]]; then
 | |
| 	# It is a definite classifier, let's call the rotator function.
 | |
|         rotate_class "$CLASSES_PATTERN"
 | |
|     else
 | |
| 	# Needs further exploring.
 | |
| 	# Non-hidden files (but no subfolders, symlinks, etc.) matching to the pattern.
 | |
| 	local files=$(cd "$BACKUP_FOLDER"; \
 | |
|                       ls -1 -t --file-type | "$XARGS" -0 | "$EGREP" "$CLASSES_PATTERN" )
 | |
| 	# Selects the qualifier substrings which actually have matching files.
 | |
| 	local classes=$(echo -e "$files" | "$SED" -E "s/$CLASSES_PATTERN/\1/" | "$SORT" -u)
 | |
| 	# Enumerates these qualifiers.
 | |
| 	for class in $classes
 | |
| 	do
 | |
| 	    # This is same as the CLASSES_PATTERN but contains the definite qualifier instead of
 | |
| 	    # the parenthesised expression - e.g one of tgz and log instad of (tgz|log)
 | |
| 	    local class_pattern=$(echo -e "$CLASSES_PATTERN" | "$SED" -E "s/\([^)]*\)/$class/") #"
 | |
| 	    # Recurses for further exploring.
 | |
| 	    rotate_classes "$class_pattern"
 | |
| 	done
 | |
|     fi
 | |
| }
 | |
| 
 | |
| # Rotates the classes, subclasses and so on with a recursive function call.
 | |
| if [ -z "$CLASSES_PATTERN" ]; then
 | |
|     # All files considered within the same class.
 | |
|     rotate_class
 | |
| else
 | |
|     # Tries to validate the pattern (loosely).
 | |
|     echo "test" | "$EGREP" "$CLASSES_PATTERN" >/dev/null 2>&1
 | |
|     [[ $? -gt 1 ]] && echo -e "$MSG_BADPATTERN $CLASSES_PATTERN" >&2 && exit 1
 | |
|     # Seems to be valid, go on!
 | |
|     rotate_classes "$CLASSES_PATTERN"
 | |
| fi
 | |
| # A final thought about the dry run.
 | |
| [[ -z "$DOIT" ]] && echo -e "$MSG_TODOIT"
 | |
| 
 | |
| # That's all, Folks :).
 |