Skip to content

Commit

Permalink
Implement path search function
Browse files Browse the repository at this point in the history
Instead of passing files (eg. using find and using xargs) go-replace can search files itself:
      --path=           use files in this path
      --path-pattern=   file pattern (* for wildcard, only basename of file)
      --path-regex=     file pattern (regex, full path)

Fixes #3
  • Loading branch information
mblaschke committed Apr 4, 2017
1 parent 110daa0 commit 03c4744
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 9 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# goreplace

[![GitHub release](https://img.shields.io/github/release/webdevops/goreplace.svg)](https://github.com/webdevops/goreplace/releases)
[![license](https://img.shields.io/github/license/webdevops/goreplace.svg)](https://github.com/webdevops/goreplace/blob/master/LICENSE)

Expand All @@ -7,6 +8,7 @@ Cli utility for replacing text in files, written in golang and compiled for usag
Inspired by https://github.com/piranha/goreplace

## Usage

```
Usage:
goreplace
Expand All @@ -18,6 +20,10 @@ Application Options:
--replace-line replace whole line instead of only match
--regex treat pattern as regex
--regex-backrefs enable backreferences in replace term
--regex-posix parse regex term as POSIX regex
--path= use files in this path
--path-pattern= file pattern (* for wildcard, only basename of file)
--path-regex= file pattern (regex, full path)
-v, --verbose verbose mode
--dry-run dry run mode
-V, --version show version and exit
Expand All @@ -41,3 +47,4 @@ GOREPLACE_VERSION=0.2.1 \
&& wget -O /usr/local/bin/go-replace https://github.com/webdevops/goreplace/releases/download/$GOREPLACE_VERSION/gr-64-linux \
&& chmod +x /usr/local/bin/go-replace
```

90 changes: 81 additions & 9 deletions goreplace.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"bytes"
"io/ioutil"
"path/filepath"
"bufio"
"os"
"strings"
Expand All @@ -26,12 +27,17 @@ var opts struct {
Regex bool ` long:"regex" description:"treat pattern as regex"`
RegexBackref bool ` long:"regex-backrefs" description:"enable backreferences in replace term"`
RegexPosix bool ` long:"regex-posix" description:"parse regex term as POSIX regex"`
Path string ` long:"path" description:"use files in this path"`
PathPattern string ` long:"path-pattern" description:"file pattern (* for wildcard, only basename of file)"`
PathRegex string ` long:"path-regex" description:"file pattern (regex, full path)"`
Verbose bool `short:"v" long:"verbose" description:"verbose mode"`
DryRun bool ` long:"dry-run" description:"dry run mode"`
ShowVersion bool `short:"V" long:"version" description:"show version and exit"`
ShowHelp bool `short:"h" long:"help" description:"show this help message"`
}

var pathFilterDirectories = []string{"autom4te.cache", "blib", "_build", ".bzr", ".cdv", "cover_db", "CVS", "_darcs", "~.dep", "~.dot", ".git", ".hg", "~.nib", ".pc", "~.plst", "RCS", "SCCS", "_sgbak", ".svn", "_obj", ".idea"}

// Replace line (if match is found) in file
func replaceInFile(filepath string) {
// try open file
Expand Down Expand Up @@ -173,7 +179,62 @@ func buildSearchTerm() {
}
}

func handleSpecialCliOptions(argparser *flags.Parser, args []string) {
// check if string is contained in an array
func contains(slice []string, item string) bool {
set := make(map[string]struct{}, len(slice))
for _, s := range slice {
set[s] = struct{}{}
}

_, ok := set[item]
return ok
}

// search files in path
func searchFilesInPath(path string, callback func(os.FileInfo, string)) {
var pathRegex *regexp.Regexp

// --path-regex
if (opts.PathRegex != "") {
pathRegex = regexp.MustCompile(opts.PathRegex)
}

// collect all files
filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
filename := f.Name()

// skip directories
if f.IsDir() {
if contains(pathFilterDirectories, f.Name()) {
return filepath.SkipDir
}

return nil
}

if (opts.PathPattern != "") {
matched, _ := filepath.Match(opts.PathPattern, filename)
if (!matched) {
return nil
}
}

if pathRegex != nil {
if (!pathRegex.MatchString(path)) {
return nil
}
}

callback(f, path)
return nil
})
}

// handle special cli options
// eg. --help
// --version
// --path
func handleSpecialCliOptions(argparser *flags.Parser, args []string) ([]string) {
// --version
if (opts.ShowVersion) {
fmt.Printf("goreplace version %s\n", Version)
Expand All @@ -186,31 +247,42 @@ func handleSpecialCliOptions(argparser *flags.Parser, args []string) {
os.Exit(1)
}

// missing any files
if (len(args) == 0) {
err := errors.New("No files specified")
logError(err)
fmt.Println()
argparser.WriteHelp(os.Stdout)
os.Exit(1)
// --path
if (opts.Path != "") {
searchFilesInPath(opts.Path, func(f os.FileInfo, path string) {
args = append(args, path)
})
}
return args
}

func main() {
var argparser = flags.NewParser(&opts, flags.PassDoubleDash)
args, err := argparser.Parse()

handleSpecialCliOptions(argparser, args)
args = handleSpecialCliOptions(argparser, args)

// check if there is an parse error
if err != nil {
logError(err)
fmt.Println()
argparser.WriteHelp(os.Stdout)
os.Exit(1)
}

// check if there is at least one file to process
if (len(args) == 0) {
err := errors.New("No files specified")
logError(err)
fmt.Println()
argparser.WriteHelp(os.Stdout)
os.Exit(1)
}

// build regex search term
buildSearchTerm()

// process file list
for i := range args {
var file string
file = args[i]
Expand Down

0 comments on commit 03c4744

Please sign in to comment.