Last active
March 20, 2023 20:20
-
-
Save hhsprings/710c51e83a143b58c5ef9ce245eb53d1 to your computer and use it in GitHub Desktop.
clean-up files in temporary directory, written by Golang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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