Skip to content

Instantly share code, notes, and snippets.

@szampardi
Last active February 29, 2024 00:25
Show Gist options
  • Save szampardi/67b4364eafe1966b2f2afd19e9827626 to your computer and use it in GitHub Desktop.
Save szampardi/67b4364eafe1966b2f2afd19e9827626 to your computer and use it in GitHub Desktop.
cleanup file and dir names in a given path
package main
import (
"flag"
"fmt"
"os"
"path"
"regexp"
"strings"
"unicode"
)
var (
notADrill *bool = flag.Bool("x", false, "actually run renames instead of just printing")
recurse *bool = flag.Bool("r", false, "recurse subdirectories")
exitOnErr *bool = flag.Bool("e", false, "exit on first error encountered")
)
func main() {
flag.Parse()
args := flag.Args()
if len(args) < 1 {
os.Exit(0)
}
stat, err := os.Stat(args[0])
if err != nil {
panic(err)
}
if !stat.IsDir() {
n := stat.Name()
cn := cleanName(n)
if n != cn {
fmt.Fprintf(os.Stderr, "rename %s -> %s\n", n, cn)
}
os.Exit(0)
}
if errs := walkDir(args[0], func(dirName, fileName string) error {
fn2 := cleanName(fileName)
if fn2 == fileName || fn2 == dirName {
return nil
}
if *notADrill {
return os.Rename(path.Join(dirName, fileName), path.Join(dirName, fn2))
}
fmt.Fprintf(os.Stdout, "%s -> %s\n", path.Join(dirName, fileName), path.Join(dirName, fn2))
return nil
}, *recurse, *exitOnErr); len(errs) > 0 {
for _, err := range errs {
fmt.Fprintf(os.Stderr, "%s\n", err)
}
os.Exit(2)
}
os.Exit(0)
}
func walkDir(base string, f func(dirPath, fileName string) error, recurse, exitOnErr bool) (errs map[string]error) {
errs = map[string]error{}
dir, err := os.ReadDir(base)
if err != nil {
errs[base] = err
return
}
for _, t := range dir {
name := t.Name()
j := path.Join(base, name)
if err := f(base, name); err != nil {
errs[j] = err
if exitOnErr {
return
}
continue
}
if t.IsDir() && recurse {
for p, err := range walkDir(j, f, recurse, exitOnErr) {
errs[p] = err
if exitOnErr {
return
}
}
}
}
return nil
}
func cleanName(s string) string {
s = strings.Map(func(r rune) rune {
if unicode.IsGraphic(r) {
return r
}
return -1
}, s)
s = strings.ToValidUTF8(s, "")
ext := path.Ext(s)
s = strings.TrimSuffix(s, ext)
for k, repls := range map[string][]string{
"_": {
"!",
"$",
"%",
"(", ")",
"[", "]",
"{", "}",
},
"-": {
" ",
",",
},
} {
for _, repl := range repls {
s = strings.ReplaceAll(s, repl, k)
}
}
s = regexp.MustCompile(`[^a-zA-Z0-9\-_\.]+`).ReplaceAllString(s, "")
s = regexp.MustCompile(`[^a-zA-Z0-9]{2,}`).ReplaceAllString(s, "_")
s = regexp.MustCompile(`[^a-zA-Z0-9]*$`).ReplaceAllString(s, "")
s = regexp.MustCompile(`^[^a-zA-Z0-9\.]`).ReplaceAllString(s, "")
if ext != "" {
s = fmt.Sprintf("%s%s", s, ext)
}
return s
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment