Skip to content

Instantly share code, notes, and snippets.

@okomestudio
Created April 1, 2020 06:20
Show Gist options
  • Save okomestudio/fae6254d3ed4e3751ab6da8144464e83 to your computer and use it in GitHub Desktop.
Save okomestudio/fae6254d3ed4e3751ab6da8144464e83 to your computer and use it in GitHub Desktop.
rmold: Remove only old files
#!/usr/bin/env bash
set -e
readonly scriptname="${0##*/}"
function usage() {
cat <<USAGE >&2
Usage: $scriptname EXPIRE FILE
Remove files whose last modified time is older than the expiry time,
relative to the current time.
EXPIRE Expiry time in seconds
FILE File(s) to be removed if expired
Version: 20.3.0
USAGE
exit "${1:-1}"
}
function error() {
>&2 echo "$scriptname: $1"
>&2 echo "Try '$scriptname -h' for more information."
exit "${2:-1}"
}
function get_last_modified() {
local filename=$1
lm=$(stat "$filename" | grep 'Modify:')
lm=${lm#"Modify: "}
lm=$(date --date="$lm" +'%s')
echo "$lm"
}
function main() {
local expire=$1
declare -a path=("${@:2}")
[[ ! "$expire" =~ ^[0-9]+$ ]] && error "first argument must be a positive integer"
[ ${#path[@]} -eq 0 ] && error "missing path"
local now
local cutoff
now=$(date +%s)
cutoff=$((now - expire))
declare -a badfiles
for filename in "${path[@]}" ; do
if [ -f "$filename" ]; then
local last_modified
last_modified=$(get_last_modified "$filename")
if [ "$cutoff" -gt "$last_modified" ]; then
rm "$filename"
fi
else
if [ ! -e "$filename" ]; then
break
fi
badfiles+=("$filename")
fi
done
if [ ${#badfiles[@]} -gt 0 ]; then
for i in "${badfiles[@]}"; do
if [ -d "$i" ]; then
>&2 echo "$scriptname: cannot remove $i: Is a directory"
else
>&2 echo "$scriptname: cannot remove $i: Not a regular file"
fi
done
exit 1
fi
}
if [ "$0" = "${BASH_SOURCE[0]}" ]; then
while getopts "h" opt; do
case $opt in
h|\?)
if [ "$opt" = "h" ]; then usage 0; else usage; fi
;;
esac
done
shift $((OPTIND - 1))
main "$@"
fi
#!/usr/bin/env bats
set -e
function setup() {
testdir=$(mktemp -d "$BATS_TMPDIR/test-rmold-XXXXX")
}
function teardown() {
rm -rf "$testdir"
}
@test "Missing both EXPIRE and PATH" {
run bin/rmold
[ "$status" -eq 1 ]
}
@test "EXPIRE is not an integer" {
run bin/rmold sfe $testdir/*
[ "$status" -eq 1 ]
}
@test "Missing path" {
run bin/rmold 2
[ "$status" -eq 1 ]
}
@test "Path is directory" {
run bin/rmold 2 $testdir
[ "$status" -eq 1 ]
}
@test "Directory has no content" {
run bin/rmold 1 $testdir/*
[ "$status" -eq 0 ]
}
@test "Path is a file to expire" {
touch $testdir/expire
sleep 2
run bin/rmold 1 $testdir/expire
[ "$status" -eq 0 ]
[ ! -e $testdir/expire ]
}
@test "Path is a file not to expire" {
touch $testdir/remain
run bin/rmold 1 $testdir/remain
[ "$status" -eq 0 ]
[ -e $testdir/remain ]
}
@test "Remove only expired files" {
touch $testdir/expire
sleep 2
touch $testdir/remain
run bin/rmold 1 $testdir/*
[ "$status" -eq 0 ]
[ ! -e $testdir/expire ]
[ -e $testdir/remain ]
}
@test "Remove files only with specific extension" {
touch $testdir/expire.log
touch $testdir/remain.txt
sleep 2
run bin/rmold 1 $testdir/*.log
[ "$status" -eq 0 ]
[ ! -e $testdir/expire.log ]
[ -e $testdir/remain.txt ]
}
@test "Remove files but skip directories" {
mkdir $testdir/remain
touch $testdir/expire
sleep 2
run bin/rmold 1 $testdir/*
[ "$status" -eq 1 ]
[ ! -e $testdir/expire ]
[ -e $testdir/remain ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment