Skip to content

Instantly share code, notes, and snippets.

@hhsprings
Last active March 20, 2023 20:20
Show Gist options
  • Save hhsprings/710c51e83a143b58c5ef9ce245eb53d1 to your computer and use it in GitHub Desktop.
Save hhsprings/710c51e83a143b58c5ef9ce245eb53d1 to your computer and use it in GitHub Desktop.
clean-up files in temporary directory, written by Golang
package main
import (
"os"
"io"
"io/fs"
"fmt"
"log"
"time"
"bytes"
"regexp"
"strconv"
"strings"
"path/filepath"
//"syscall"
"encoding/json"
"text/template"
"github.com/danwakefield/fnmatch"
"github.com/ogier/pflag"
)
type TempCleanTarget struct {
TargetDir string
Pattern string
ExcludePattern string
TargetAge string
}
type TempCleanGoConfig struct {
Targets []TempCleanTarget
}
func (t *TempCleanTarget) Filter(ent fs.DirEntry) bool {
fnmflag := fnmatch.FNM_PATHNAME | fnmatch.FNM_IGNORECASE
res := true
if t.Pattern != "" {
matched := false
for _, pat := range strings.Split(t.Pattern, ";") {
matched = matched || fnmatch.Match(pat, ent.Name(), fnmflag)
}
res = res && matched
}
if t.ExcludePattern != "" {
matched := false
for _, pat := range strings.Split(t.ExcludePattern, ";") {
matched = matched || !fnmatch.Match(pat, ent.Name(), fnmflag)
}
res = res && matched
}
if res {
re := regexp.MustCompile(`([<>]=?)\s*(\d+)\s*(seconds|minutes|hours|days|weeks)?`)
tades := re.FindAllStringSubmatch(t.TargetAge, -1)
if len(tades) == 1 && len(tades[0][1:]) >= 2 {
ta := tades[0][1:]
op := ta[0]
age, _ := strconv.ParseInt(ta[1], 10, 0)
if len(ta) == 3 {
switch ta[2] {
case "seconds":
age *= 1
break
case "minutes":
age *= 60
break
case "hours":
age *= 60*60
break
case "days":
age *= 60*60*24
break
case "weeks":
age *= 60*60*24*7
break
}
}
finf, _ := ent.Info()
//ct, mt := w32fatimes(finf)
mt := finf.ModTime()
switch op {
case ">":
res = (now - mt.Unix() > age)
break
case ">=":
res = (now - mt.Unix() >= age)
break
case "<":
res = (now - mt.Unix() < age)
break
case "<=":
res = (now - mt.Unix() <= age)
break
}
}
}
return res
}
var (
now int64
conf TempCleanGoConfig
)
const (
CONF_DEFAULT = `{{/*
configuration of tempcleango.
basically this file is a json-like format.
*/}}
{
"Targets": [
{
"TargetDir": "{{.Getenv "TEMP"}}",
"Pattern": "*.*",
"ExcludePattern": "",
"TargetAge": ">1days"
}
{{/*
, {
"TargetDir": "c:/Windows/Temp",
"Pattern": "*.log",
"ExcludePattern": "",
"TargetAge": ">4weeks"
}
*/}}
]
}
`
)
type ConfigfileExport struct {
}
func (exp ConfigfileExport) Getenv(key string) string {
return os.Getenv(key)
}
func init() {
now = time.Now().Unix()
ousage := pflag.Usage
pflag.Usage = func() {
ousage()
os.Exit(1)
}
cfgalt := pflag.StringP(
"config_alternative", "C", "", "use alternative config")
pflag.Parse()
getconf := func () string {
var confpath string
if *cfgalt != "" {
confpath = *cfgalt
} else {
home := os.Getenv("USERPROFILE")
if home == "" {
home = os.Getenv("HOME")
}
confpath = filepath.Join(home, ".tempcleango.go.conf.json")
}
_, err := os.Stat(confpath)
var f *os.File
if err != nil {
f, _ = os.OpenFile(confpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0744)
fmt.Fprintf(f, "%s\n", CONF_DEFAULT)
f.Close()
if *cfgalt != "" {
log.Print(
"`",
*cfgalt,
"'",
" was not found on your system, so i created it for default configuration. ",
"Check and edit it as what you want, and re-run `tempcleango'.")
os.Exit(1)
}
}
f, _ = os.Open(confpath)
defer f.Close()
cont, _ := io.ReadAll(f)
return string(cont)
}
conftmpl, err := template.New("tempcleangoconf").Parse(getconf())
if err != nil {
panic(err)
}
var tmplout bytes.Buffer
err = conftmpl.Execute(&tmplout, ConfigfileExport{})
if err != nil {
panic(err)
}
json.Unmarshal(tmplout.Bytes(), &conf)
}
/*
func w32fatimes(finf fs.FileInfo) (int64, int64) {
d := finf.Sys().(*syscall.Win32FileAttributeData)
return d.CreationTime.Nanoseconds() / 1e+9, finf.ModTime().Unix()
}
*/
func rmfiles(t TempCleanTarget) {
ents, err := os.ReadDir(t.TargetDir)
if err != nil {
fmt.Print(err)
return
}
for _, ent := range ents {
path := filepath.ToSlash(filepath.Join(t.TargetDir, ent.Name()))
if ent.IsDir() {
tin := t /* deep copy */
tin.TargetDir = path
rmfiles(tin)
} else if t.Filter(ent) {
finf, _ := ent.Info()
mt := finf.ModTime()
pathrepr := fmt.Sprint(path, "(", mt.Format(time.RFC3339), ")")
rmerr := os.Remove(path)
if rmerr != nil {
fmt.Print(pathrepr, ": ", rmerr, "\n")
} else {
fmt.Print("removed `", pathrepr, "'\n")
}
}
}
}
func main() {
for _, t := range conf.Targets {
rmfiles(t)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment