Skip to content

Instantly share code, notes, and snippets.

@yashtibrewal
Created May 25, 2022 12:50
Show Gist options
  • Save yashtibrewal/e8a59d40617df74ae07bdfcf283dda01 to your computer and use it in GitHub Desktop.
Save yashtibrewal/e8a59d40617df74ae07bdfcf283dda01 to your computer and use it in GitHub Desktop.
Multithreading Alternate (ZTM)
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strings"
"sync"
)
//--Summary:
// Create a grep clone that can do simple substring searching
// within files. It must auto-recurse into subdirectories.
//
//--Requirements:
//* Use goroutines to search through the files for a substring match
//* Display matches to the terminal as they are found
// * Display the line number, file path, and complete line containing the match
//* Recurse into any subdirectories looking for matches
//* Use any synchronization method to ensure that all files
// are searched, and all results are displayed before the program
// terminates.
//
//--Notes:
//* Program invocation should follow the pattern:
// mgrep search_string search_dir
/*
Solution:
1. A function which recieves a handler to a file and searches for the word in the file
2. A function which iterates through all the files a directory and recurses subdirectories
*/
type SearchResult struct {
lineNumber int
filePath string
line string
}
func (r *SearchResult) String() string {
return fmt.Sprintf("Line Number: %d, File Path: %s, Line: %v", r.lineNumber, r.filePath, r.line)
}
type SearchResults struct {
mutex sync.Mutex
waitGroup sync.WaitGroup
searchResults []SearchResult
}
func searchFile(filePath string, searchString string, searchResults *SearchResults) {
// Read the file line by line
// check if the line contains the search string
fmt.Println(filePath, " opening")
fileHandle, err := os.Open(filePath)
if err != nil {
fmt.Printf("Error reading file %v %v\n", filePath, err.Error())
}
defer fileHandle.Close()
fileScanner := bufio.NewScanner(fileHandle)
fileScanner.Split(bufio.ScanLines)
lineNumber := 0
for fileScanner.Scan() {
lineNumber++
line := fileScanner.Text()
found := strings.Contains(line, searchString)
if found {
searchResults.mutex.Lock()
searchResult := SearchResult{
lineNumber: lineNumber,
filePath: filePath,
line: line,
}
searchResults.searchResults = append(searchResults.searchResults, searchResult)
searchResults.mutex.Unlock()
}
}
searchResults.waitGroup.Done()
}
func analyzeDirector(dir string, searchString *string, searchResults *SearchResults) {
files, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Println(err.Error())
}
for _, file := range files {
if !file.IsDir() { // call the go routune in this file
searchResults.waitGroup.Add(1)
go searchFile(file.Name(), *searchString, searchResults)
} else {
analyzeDirector(file.Name(), searchString, searchResults)
}
}
searchResults.waitGroup.Wait()
}
func main() {
args := os.Args
args = args[1:] // remove the program name
if len(args) != 2 {
fmt.Printf("Please enter the directory and search string")
return
}
searchString := args[0]
directory := args[1]
searchResults := SearchResults{}
fmt.Println("Starting the search on files")
analyzeDirector(directory, &searchString, &searchResults)
fmt.Println("Search Completed")
for i := 0; i < len(searchResults.searchResults); i++ {
result := searchResults.searchResults[i]
fmt.Println(result)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment