package main import ( "bufio" "fmt" "io/fs" "log" "os" "path/filepath" "regexp" ) // ChannelBuffering is the size of file processing results queue const ChannelBuffering = 10 // FileMatchData contains grep results type FileMatchData struct { Path string LineIndexes []int Lines []string } // ProcessSingleFile processes file and send it Result to channel func ProcessSingleFile(rePattern regexp.Regexp, path string, out chan FileMatchData) { file, err := os.Open(path) if err != nil { log.Fatal(err) } defer file.Close() res := FileMatchData{Path: path, LineIndexes: make([]int, 0), Lines: make([]string, 0)} scanner := bufio.NewScanner(file) // optionally, resize scanner's capacity for lines over 64K, see next example curLineInd := 1 for scanner.Scan() { lineText := scanner.Text() if rePattern.FindString(lineText) != "" { res.LineIndexes = append(res.LineIndexes, curLineInd) res.Lines = append(res.Lines, lineText) } curLineInd++ } if err := scanner.Err(); err != nil { fmt.Printf("Error %v with file %v\n", err, path) } out <- res } func main() { pathPattern := "git/hook" rePathPattern, err := regexp.Compile(pathPattern) if err != nil { fmt.Printf("Unable to compile path pattern Regexp: %v\n", pathPattern) return } textPattern := "the commit" reTextPattern, err := regexp.Compile(textPattern) if err != nil { fmt.Printf("Unable to compile text pattern Regexp: %v\n", textPattern) return } processedChannel := make(chan FileMatchData, ChannelBuffering) totalFiles := 0 err = filepath.Walk(".", func(path string, info fs.FileInfo, err error) error { if err != nil { fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err) return err } if rePathPattern.FindString(path) == "" { return nil } // if info.IsDir() && info.Name() == subDirToSkip { // fmt.Printf("skipping a dir without errors: %+v \n", info.Name()) // return filepath.SkipDir // } if info.IsDir() { return nil } go ProcessSingleFile(*reTextPattern, path, processedChannel) totalFiles++ return nil }) for ; totalFiles > 0; totalFiles-- { curProcessed := <-processedChannel if len(curProcessed.Lines) == 0 { continue } fmt.Printf("%s:\n", curProcessed.Path) for i := 0; i < len(curProcessed.Lines); i++ { fmt.Printf("%d:%s\n", curProcessed.LineIndexes[i], curProcessed.Lines[i]) } fmt.Print("\n") } if err != nil { fmt.Printf("error walking the path %q: %v\n", pathPattern, err) return } }