Skip to content

Commit 44c82b3

Browse files
committed
Allow positional arguments for pattern
1 parent 1c84772 commit 44c82b3

File tree

4 files changed

+186
-136
lines changed

4 files changed

+186
-136
lines changed

Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
# Generic build command removing CGO and debugging info for smaller release
22
build:
3-
CGO_ENABLED=0 go build -o gf -ldflags="-s -w" main.go
3+
CGO_ENABLED=0 go build -o gof -ldflags="-s -w" ./*.go
4+
5+
# Format code
6+
fmt:
7+
go fmt ./...
8+
9+
# check the code for mistakes
10+
lint:
11+
go vet ./...
412

513
#This will build the binary specifically for newer x86-64 machines. It assumes the CPU
614
#is newer like most modern desktop cpus, hence amd64 lvl v3
715
# Docs: https://github.com/golang/go/wiki/MinimumRequirements#amd64
816
# With a small program like this we likely won't see a speedup but I like the opporunity
917
# to go fast
1018
buildx86:
11-
GOARCH=amd64 GOAMD64="v3" CGO_ENABLED=0 go build -o gf -ldflags="-s -w" main.go
19+
GOARCH=amd64 GOAMD64="v3" CGO_ENABLED=0 go build -o gof -ldflags="-s -w" ./*.go

cli.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"runtime"
6+
)
7+
8+
// Config the programs configuration used to set behavior
9+
type Config struct {
10+
QueueSize int
11+
MaxResults int
12+
Workers int
13+
Dir string
14+
Pattern string
15+
Insensative bool
16+
Version bool
17+
}
18+
19+
func parseCLI() *Config {
20+
//The number of workers. If there are more workers the system can read from
21+
//the work queue more often and a larger queue is not required.
22+
workers := flag.Int("w", -1, "Number of workers")
23+
//If the queue overflows we'll use a slice to store work which might slow the system
24+
queueSize := flag.Int("q", 512, "The max work queue size")
25+
maxResults := flag.Int("c", -1, "The maximum number of results to find")
26+
dir := flag.String("d", ".", "The starting directory to check for files")
27+
pattern := flag.String("p", "", "A pattern to check for within the file names")
28+
insensative := flag.Bool("i", true, "perform a case insensative search")
29+
v := flag.Bool("v", false, "print the version and build information")
30+
flag.Parse()
31+
32+
p := *pattern
33+
if p == "" {
34+
p = flag.Arg(0)
35+
}
36+
37+
w := *workers
38+
if *workers <= 0 {
39+
// magic 2, anecdotal evidence of better performance over NumCPU
40+
w = runtime.NumCPU() + 2
41+
}
42+
43+
//Only for OSX/Linux, sorry windows
44+
//Remove any trailing slashes in the path
45+
if (*dir)[len(*dir)-1:] == "/" {
46+
*dir = string((*dir)[0 : len(*dir)-1])
47+
}
48+
49+
return &Config{
50+
QueueSize: *queueSize,
51+
MaxResults: *maxResults,
52+
Workers: w,
53+
Dir: *dir,
54+
Pattern: p,
55+
Insensative: *insensative,
56+
Version: *v,
57+
}
58+
}

main.go

Lines changed: 9 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
package main
22

33
import (
4-
"flag"
54
"fmt"
6-
"os"
7-
"runtime"
8-
"strings"
9-
"sync"
105
)
116

127
var IGNORE_PATHS = [7]string{
@@ -31,41 +26,20 @@ func printBuildInfo() {
3126
}
3227

3328
func main() {
34-
//The number of workers. If there are more workers the system can read from
35-
//the work queue more often and a larger queue is not required.
36-
workers := flag.Int("w", -1, "Number of workers")
37-
//If the queue overflows we'll use a slice to store work which might slow the system
38-
queueSize := flag.Int("q", 512, "The max work queue size")
39-
maxResults := flag.Int("c", -1, "The maximum number of results to find")
40-
dir := flag.String("d", ".", "The starting directory to check for files")
41-
pattern := flag.String("p", "", "A pattern to check for within the file names")
42-
v := flag.Bool("v", false, "print the version and build information")
43-
flag.Parse()
44-
45-
if *v {
29+
cfg := parseCLI()
30+
if cfg.Version {
4631
printBuildInfo()
4732
return
4833
}
4934

50-
if *pattern == "" {
51-
fmt.Println("No pattern provided")
35+
if cfg.Pattern == "" {
36+
fmt.Println("No pattern found, please provide a search pattern")
5237
return
5338
}
5439

55-
w := *workers
56-
if *workers <= 0 {
57-
w = runtime.NumCPU() + 2
58-
}
59-
60-
//Only for OSX/Linux, sorry windows
61-
//Remove any trailing slashes in the path
62-
if (*dir)[len(*dir)-1:] == "/" {
63-
*dir = string((*dir)[0 : len(*dir)-1])
64-
}
65-
66-
printCh := make(chan string, w)
40+
printCh := make(chan string, cfg.Workers)
6741
//The system will reach deadlock if the work queue reaches capacity
68-
workQ := make(chan string, *queueSize)
42+
workQ := make(chan string, cfg.QueueSize)
6943
//To avoid deadlock, send tasks here which will have a non-blocky retry
7044
//func to add tasks back to workQ
7145
failover := make(chan string)
@@ -77,110 +51,11 @@ func main() {
7751
//defer close(dirCount)
7852
//defer close(failover)
7953
go handleFailover(workQ, failover)
80-
go createWorkerPool(pattern, workQ, failover, printCh, dirCount, w)
54+
go createWorkerPool(cfg.Pattern, workQ, failover, printCh, dirCount, cfg.Workers)
8155

8256
//Send first work request
83-
workQ <- *dir
57+
workQ <- cfg.Dir
8458

8559
//Print all results
86-
showResults(printCh, maxResults)
87-
}
88-
89-
func showResults(ch chan string, limit *int) {
90-
if *limit > 0 {
91-
n := 0
92-
for item := range ch {
93-
fmt.Println(item)
94-
n++
95-
if n >= *limit {
96-
return
97-
}
98-
}
99-
} else {
100-
for item := range ch {
101-
fmt.Println(item)
102-
}
103-
}
104-
}
105-
106-
func dirChecker(in chan int, work chan string) {
107-
n := 1
108-
for i := range in {
109-
n += i
110-
if n <= 0 {
111-
close(work)
112-
return
113-
}
114-
}
115-
}
116-
117-
func createWorkerPool(p *string, in chan string, failover chan string, results chan string, cnt chan int, w int) {
118-
var wg sync.WaitGroup
119-
for i := 0; i < w; i++ {
120-
wg.Add(1)
121-
go func() {
122-
defer wg.Done()
123-
search(p, in, failover, results, cnt)
124-
}()
125-
}
126-
wg.Wait()
127-
close(results)
128-
}
129-
func search(pattern *string, in chan string, failover chan string, out chan string, cnt chan int) {
130-
for path := range in {
131-
items, err := os.ReadDir(path)
132-
if err != nil {
133-
fmt.Println("Error reading the directory", path)
134-
fmt.Println(err)
135-
cnt <- -1
136-
}
137-
ItemSearch:
138-
for _, item := range items {
139-
if item.IsDir() {
140-
//Don't dive into directories I don't care about
141-
for _, p := range IGNORE_PATHS {
142-
if p == item.Name() {
143-
continue ItemSearch
144-
}
145-
}
146-
subPath := fmt.Sprintf("%s/%s", path, item.Name())
147-
cnt <- 1
148-
select {
149-
case in <- subPath:
150-
case failover <- subPath:
151-
}
152-
}
153-
//Always check if the name of the thing matches pattern, including directory names
154-
if strings.Index(item.Name(), *pattern) >= 0 {
155-
//subPath is repeated but no point in creating an allocation if not required
156-
subPath := fmt.Sprintf("%s/%s", path, item.Name())
157-
out <- subPath
158-
}
159-
160-
}
161-
//We finished reading everything in the dir, tell the accounted we finished
162-
cnt <- -1
163-
}
164-
}
165-
166-
func handleFailover(work, fail chan string) {
167-
var q []string
168-
for {
169-
task := <-fail
170-
q = append(q, task)
171-
//TODO: Add verbose logging here so users can check if the failover was used
172-
for {
173-
select {
174-
case work <- q[0]:
175-
q = q[1:]
176-
case task := <-fail:
177-
q = append(q, task)
178-
default:
179-
}
180-
//I don't know if we'll get an issue with `work <- q[0]` unless we have this
181-
if len(q) == 0 {
182-
break
183-
}
184-
}
185-
}
60+
showResults(printCh, &cfg.MaxResults)
18661
}

search.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
"sync"
8+
)
9+
10+
func showResults(ch chan string, limit *int) {
11+
if *limit > 0 {
12+
n := 0
13+
for item := range ch {
14+
fmt.Println(item)
15+
n++
16+
if n >= *limit {
17+
return
18+
}
19+
}
20+
return
21+
}
22+
23+
for item := range ch {
24+
fmt.Println(item)
25+
}
26+
}
27+
28+
func dirChecker(in chan int, work chan string) {
29+
n := 1
30+
for i := range in {
31+
n += i
32+
if n <= 0 {
33+
close(work)
34+
return
35+
}
36+
}
37+
}
38+
39+
func createWorkerPool(p string, in chan string, failover chan string, results chan string, cnt chan int, w int) {
40+
var wg sync.WaitGroup
41+
for i := 0; i < w; i++ {
42+
wg.Add(1)
43+
go func() {
44+
defer wg.Done()
45+
search(p, in, failover, results, cnt)
46+
}()
47+
}
48+
wg.Wait()
49+
close(results)
50+
}
51+
52+
func search(pattern string, in chan string, failover chan string, out chan string, cnt chan int) {
53+
for path := range in {
54+
items, err := os.ReadDir(path)
55+
if err != nil {
56+
fmt.Println("Error reading the directory", path)
57+
fmt.Println(err)
58+
cnt <- -1
59+
}
60+
ItemSearch:
61+
for _, item := range items {
62+
if item.IsDir() {
63+
//Don't dive into directories I don't care about
64+
for _, p := range IGNORE_PATHS {
65+
if p == item.Name() {
66+
continue ItemSearch
67+
}
68+
}
69+
subPath := fmt.Sprintf("%s/%s", path, item.Name())
70+
cnt <- 1
71+
select {
72+
case in <- subPath:
73+
case failover <- subPath:
74+
}
75+
}
76+
//Always check if the name of the thing matches pattern, including directory names
77+
if strings.Contains(item.Name(), pattern) {
78+
//subPath is repeated but no point in creating an allocation if not required
79+
subPath := fmt.Sprintf("%s/%s", path, item.Name())
80+
out <- subPath
81+
}
82+
83+
}
84+
//We finished reading everything in the dir, tell the accounted we finished
85+
cnt <- -1
86+
}
87+
}
88+
89+
func handleFailover(work, fail chan string) {
90+
var q []string
91+
for {
92+
task := <-fail
93+
q = append(q, task)
94+
//TODO: Add verbose logging here so users can check if the failover was used
95+
for {
96+
select {
97+
case work <- q[0]:
98+
q = q[1:]
99+
case task := <-fail:
100+
q = append(q, task)
101+
default:
102+
}
103+
//I don't know if we'll get an issue with `work <- q[0]` unless we have this
104+
if len(q) == 0 {
105+
break
106+
}
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)