Golang: Find Text in Directory (grep) 📜
Here's a script that does find string or multiple strings in all files in a dir.
/* File name: xah_find.go Description: find strings in a dir. Can be a list of strings Home page: http://xahlee.info/golang/goland_find_string.html Created: 2020-05-24 Version: 2025-07-03 */ 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", "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{ // "c:/Users/xah/web/xahlee_info/comp/all_x_in_unicode.html", // "c:/Users/xah/web/xahlee_info/comp/apple_unicode_support.html", // "c:/Users/xah/web/xahlee_info/comp/ascii_chars.html", } // list of string to search // each item is intepreted as regex // use regexp.QuoteMeta to quote them if want plain text var findList = []string{ // example: case insensitive // `(?i)something`, // example not using regex // regexp.QuoteMeta(`something`), regexp.QuoteMeta(`style="width:`), } // number of chars (actually bytes) to show before the found string const charsBefore = 100 const charsAfter = 100 const fileSep = "==file=sep=M6bcX=====================================" const occurSep = "--occur-sep-Xy5Xb-----------------------" 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, "\n") } 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
If you use emacs:
- Bookmark the golang script so you can easily open it when you need find replace. 〔see Emacs: Bookmark〕
- Alt+x
xah-run-current-file
to run the script. 〔see Emacs: Run Current File 📜〕
- Install Emacs: Xah Find Replace (xah-find.el) 📦
- After you run the script, Alt+x
xah-find-output-mode
. It highlight occurrences and you can press enter to the file location.
If you are using Emacs: Xah Fly Keys 📦, many of the command above is already there. You still need to install xah-find.el for coloring the output.
Find Replace Scripts
- Golang: Find Text in Directory (grep) 📜
- Golang: Regex Find Replace Text in Directory 📜
- Python: Find Replace Text in Directory 📜
- Python: Regex Find Replace Text in Directory 📜
- Perl: Find Replace Text in Directory 📜
- Emacs: Find Replace Text in Directory
- Emacs: Xah Find Replace (xah-find.el) 📦
- Wolfram: Find Replace Text 📜