Golang: Find String (grep) Script
Here's a script that does find string or multiple strings in all files in a dir.
// -*- coding: utf-8 -*- // File name: xah_find.go // Description: find strings in a dir. Can be a list of strings // website: http://xahlee.info/golang/goland_find_string.html // version 2020-05-24 2022-07-24 2022-07-29 2022-08-07 package main import ( "fmt" "io/fs" "os" "path/filepath" "regexp" "strings" "time" "unicode/utf8" ) // inDir is dir to start. must be full path var inDir = "c:/Users/xah/web/" // fnameRegex. only these are searched const fnameRegex = `\.html$` // dir name, not full path. and exact equal, not regex var dirsToSkip = []string{ ".git", "REC-SVG11-20110816", "clojure-doc-1.8", "emacs_manual", "js_es2011", "js_es2015", "node_api", } // fileList if not empty, only these are processed. Each element is a full path var fileList = []string{} // list of string to search // each item is intepreted as regex // use regexp.QuoteMeta to quote them if want plain text var findList = []string{ regexp.QuoteMeta(`haskell`), } // number of chars (actually bytes) to show before the found string const charsBefore = 100 const charsAfter = 100 const fileSep = "━━━━━filesepM6bcX━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" const occurSep = "─────occurSep5h4DP──────────────────────────────────────────" const occurBracketL = '⦋' const occurBracketR = '⦌' const posBracketL = '⁅' const posBracketR = '⁆' const fileBracketL = '❬' const fileBracketR = '❭' var bigRegexStr = strings.Join(findList, "|") var rgx = regexp.MustCompile(bigRegexStr) func printSliceStr(sliceX []string) error { for k, v := range sliceX { fmt.Printf("%v %v\n", k, v) } return nil } // scriptPath returns the current running script path // version 2018-10-07 func scriptPath() string { name, errPath := os.Executable() if errPath != nil { panic(errPath) } return name } func max(x int, y int) int { if x > y { return x } else { return y } } func min(x int, y int) int { if x > y { return y } else { return x } } // stringMatchAny return true if x equals any of y func stringMatchAny(x string, y []string) bool { for _, v := range y { if x == v { return true } } return false } func doFile(path string) error { var textBytes, er = os.ReadFile(path) if er != nil { panic(er) } var indexes = rgx.FindAllIndex(textBytes, -1) var bytesLength = len(textBytes) if len(indexes) != 0 { for _, k := range indexes { var foundStart = k[0] var foundEnd = k[1] var showStart = max(foundStart-charsBefore, 0) for !utf8.RuneStart(textBytes[showStart]) { showStart = max(showStart-1, 0) } var showEnd = min(foundEnd+charsAfter, bytesLength-1) for !utf8.RuneStart(textBytes[showEnd]) { showEnd = min(showEnd-1, bytesLength) } // fmt.Printf("%s〖%s〗%s\n", textBytes[showStart:foundStart], fmt.Printf("%c%d%c %s%c%s%c%s\n", posBracketL, utf8.RuneCount(textBytes[0:foundStart+1]), posBracketR, textBytes[showStart:foundStart], occurBracketL, textBytes[foundStart:foundEnd], occurBracketR, textBytes[foundEnd:showEnd]) fmt.Println(occurSep) } fmt.Printf("%v %c%v%c\n", len(indexes), fileBracketL, path, fileBracketR) fmt.Println(fileSep, "\n") } return nil } var f_walkfunc = func(xpath string, xinfo fs.DirEntry, xerr error) error { // first thing to do, check error. and decide what to do about it if xerr != nil { fmt.Printf("error [%v] at a path [%q]\n", xerr, xpath) return xerr } if xinfo.IsDir() { if stringMatchAny(filepath.Base(xpath), dirsToSkip) { return filepath.SkipDir } } else { var x, err = regexp.MatchString(fnameRegex, filepath.Base(xpath)) if err != nil { panic("stupid MatchString error 59767") } if x { doFile(xpath) } } return nil } func main() { fmt.Println("-*- coding: utf-8; mode: xah-find-output -*-") fmt.Printf("%v\n", time.Now()) fmt.Printf("Script: %v\n", filepath.Base(scriptPath())) fmt.Printf("in dir: %v\n", inDir) fmt.Printf("file regex filter: %v\n", fnameRegex) fmt.Printf("fileList:\n") printSliceStr(fileList) fmt.Printf("findList:\n") printSliceStr(findList) fmt.Println() fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") if len(fileList) >= 1 { for _, v := range fileList { doFile(v) } } else { err := filepath.WalkDir(inDir, f_walkfunc) if err != nil { fmt.Printf("error walking the path %q: %v\n", inDir, err) } } fmt.Println("\nDone.") }
Emacs Integration
This script is integrated with emacs.
If you use emacs:
- Install Emacs: xah-find.el, Find Replace in Pure Elisp
- bookmark the golang script so you can easily open it when you need find replace. 〔see Emacs: Bookmark〕
- Open the golang script, change input dir and find replace strings.
- Alt+x
xah-run-current-file
to run the script. 〔see Emacs: Run Current File〕 - In output, Alt+x
xah-find-output-mode
, to highlight result, and press enter on highlighted find/replace string text or file path to open the file.
If you are using Xah Fly Keys, many of the command above is already there. You still need to install xah-find.el for coloring the output.
〔see Emacs: Xah Fly Keys 📦〕
Find Replace Scripts
- Golang: Find String (grep) Script
- Golang: Script to Find Replace Multi-Pairs of Regex in a Directory
- Python: Find Replace Regex in Dir
- Perl: Find Replace String Pairs in Directory
- Emacs: Interactive Find Replace Text in Directory
- Emacs: Xah Find Replace (xah-find.el)
- WolframLang: Find/Replace Script