Fidel - Find and delete files

After writing shell scripts that find files matching NAME and/or TIME parameters and create tar archives or convert video to audio I wanted a script that could find and delete files. Example: Find all MP4 files modified in the last 7 days, provide a total count, and prompt to confirm file deletion before erasure.

In the course of creating a deletion script I ran into a problem that I had overlooked in my earlier scripts. Supplying a positional parameter with a NAME and a wildcard would have the shell expand the parameter before it was passed to the find command. So for example shellscript *.mp4 would expand the *.mp4 to all the files in the current directory that ended with that argument and pass those filenames to find. If there were no sub-folders in the directory that needed to be searched this is not a problem ... otherwise the result is that filename expansion prevents recursive search.

What I wanted to occur is have the script forego expansion and pass the literal argument ∗mp4 to search. The solution was to use the noglob variable which turns off and on shell pathname expansion. Writing a function that includes set -o noglob to turn off and set +o noglob to restore expansion allows wildcard characters to be treated literally in a variable. Recursive search is now a go ...

#!/bin/bash
#: VARIABLES
Scriptname="Fidel"
Description="[Fi]nd and [del]ete - Recursive search of PWD for matching FILE(s) and delete"
Synopsis="$Scriptname [-v|-h|-n [FILE]|-t [+-][TIME]]"
Version="0.2"
# -------- #
RequiredPrograms="findutils"
InvalidInput="is invalid input. Please select 'Y(es)' or 'N(o)' ..."
FindName=
FindTime=

#: FUNCTIONS
VersionInfo()
{
cat << _EOF_
$Scriptname, Version $Version
_EOF_
}

HelpInfo()
{
cat << _EOF_
Usage: $Synopsis
${Description}.

Debian package requirements: $RequiredPrograms

OPTIONS
-v  display version
-h  display help
-t  match files whose contents were last modified
    [+-][digit][m[inutes]|d[ays] ago
-n  match file "NAME"

EXAMPLES
# delete files modified less than 7 days ago
Fidel -t -7d
# delete files modified more than 88 minutes ago
Fidel -t +88m
# delete all JPG files (quotes are important)
Fidel -n "*jpg"
# delete all PNG files modified less than 90 minutes ago
Fidel -n "*png" -t -90m
_EOF_
}

Penguinista()
{
cat << _EOF_

(O<
(/)_
_EOF_
}

PenguinistaDone()
{
printf "\n"
printf "$( Penguinista )\tAll done.\n"
}

PenguinistaNoDelete()
{
printf "\n"
printf "$( Penguinista )\tOK. No files deleted.\n"
}

ReplyInvalid()
{
printf "\n"
printf "'$REPLY' $InvalidInput\n"
}

ModField()
{
if [[ -z $FindTime ]]
then
            ModUnits=""
else
            if [[ $FindTime =~ ^[+-]*[0-9]+[m]$ ]]
            then
                            ModUnits="-mmin ${FindTime%m}"
            elif [[ $FindTime =~ ^[+-]*[0-9]+[d]$ ]]
            then
                            ModUnits="-mtime ${FindTime%d}"
            else
                            printf "Error: '-t' option requires argument in correct format ...\n"
                            printf "\n"
                            HelpInfo
                            exit 1
            fi
fi
}

FindNameCount()
{
set -o noglob       # no pathname expansion
ModField
local counter
counter=/tmp/FindNameCount
find -L . -iname $FindName -type f $ModUnits -print 2>/dev/null | tee -a $counter
printf "Found $( wc -l $counter | awk {'print $1'} ) file(s).\n"
rm $counter
set +o noglob
}

FindTimeCount()
{
ModField
local counter
counter=$( find . -type f $ModUnits -print 2>/dev/null | tee /dev/tty | wc -l )
printf "Found $counter file(s).\n"
}

#: LET'S ROLL ...
if [[ $1 != "" ]]
then
    while getopts “n:t:hv” OPTION
            do
            case $OPTION in
                                            n )             FindName=$OPTARG
                                                            ;;
                                            t )             FindTime=$OPTARG
                                                            ;;
                                            h )             HelpInfo
                                                            exit
                                                            ;;
                                            v )             VersionInfo
                                                            exit
                                                            ;;
                                            ? )             HelpInfo
                                                            exit
                                                            ;;
            esac
    done
else
    printf "Error: Fidel requires at least 1 parameter ...\n"
            printf "\n"
    HelpInfo
    exit 1
fi

if [[ ( -n $FindTime && -n $FindName ) || -n $FindName ]]
then
            FindNameCount
            while true
            do
                            printf "\n"
                            read -n 1 -p "Delete? [yN] > "
                            if [[ "$REPLY" == [yY] ]]
                            then
                                            find -L . -iname "$FindName" -type f $ModUnits -delete
                                            PenguinistaDone
                                            exit
                            elif [[ "$REPLY" == [nN] || "$REPLY" == "" ]]
                            then
                                            PenguinistaNoDelete
                                            exit
                            else
                                            ReplyInvalid
                            fi
            done
fi

if [[ -n $FindTime ]]
then
            FindTimeCount
            while true
            do
                            printf "\n"
                            read -n 1 -p "Delete? [yN] > "
                            if [[ "$REPLY" == [yY] ]]
                            then
                                            find . -type f $ModUnits -delete
                                            PenguinistaDone
                                            exit
                            elif [[ "$REPLY" == [nN] || "$REPLY" == "" ]]
                            then
                                            PenguinistaNoDelete
                                            exit
                            else
                                            ReplyInvalid
                            fi
            done
fi

Examples

Fidel can find and delete files by NAME and/or TIME ...

# delete files modified less than 7 days ago
$ Fidel -t -7d

# delete files modified more than 88 minutes ago
$ Fidel -t +88m

# delete all JPG files (quotes are important)
$ Fidel -n "*jpg"

# delete all PNG files modified less than 90 minutes ago
$ Fidel -n "*png" -t -90m

Running Fidel -h displays available options.

This week - in addition to set noglob I learned about the tee command and getopts. Fidel's counter function totals up the number of files returned by find and uses tee to both display to screen and create a temporary file to hold the output. The shell's built-in command getopts is used to process the script's positional parameters and arguments.

More • shellprogramminglinux