Skip to content

Commit b418e59

Browse files
committed
Improve cli handling
- Add --regex-posix - Improve code documentation
1 parent 3ba503d commit b418e59

File tree

2 files changed

+70
-52
lines changed

2 files changed

+70
-52
lines changed

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# goreplace
2-
Replace cli utility written in golang
2+
Cli utility for replacing text in files, written in golang and compiled for usage in Docker images
33

44
Inspired by https://github.com/piranha/goreplace
55

@@ -23,9 +23,11 @@ Application Options:
2323

2424
### Examples
2525

26-
| Command | Description |
27-
|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
28-
| `goreplace -s foobar -r barfoo file1 file2` | Replaces `foobar` to `barfoo` in file1 and file2 |
29-
| `goreplace --regex -s 'foo.*' -r barfoo file1 file2` | Replaces the regex `foo.*` to `barfoo` in file1 and file2 |
30-
| `goreplace --regex --ignore-case -s 'foo.*' -r barfoo file1 file2` | Replaces the regex `foo.*` (and ignore case) to `barfoo` in file1 and file2 |
31-
| `goreplace --replace-line -s 'foobar' -r barfoo file1 file2` | Replaces all lines with content `foobar` to `barfoo` (whole line) in file1 and file2 |
26+
| Command | Description |
27+
|:-------------------------------------------------------------------|:-------------------------------------------------------------------------------------|
28+
| `goreplace -s foobar -r barfoo file1 file2` | Replaces `foobar` to `barfoo` in file1 and file2 |
29+
| `goreplace --regex -s 'foo.*' -r barfoo file1 file2` | Replaces the regex `foo.*` to `barfoo` in file1 and file2 |
30+
| `goreplace --regex --ignore-case -s 'foo.*' -r barfoo file1 file2` | Replaces the regex `foo.*` (and ignore case) to `barfoo` in file1 and file2 |
31+
| `goreplace --replace-line -s 'foobar' -r barfoo file1 file2` | Replaces all lines with content `foobar` to `barfoo` (whole line) in file1 and file2 |
32+
33+

goreplace.go

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
11
package main
22

33
import (
4-
"fmt"
5-
"errors"
6-
"bytes"
7-
"io/ioutil"
8-
"bufio"
9-
"os"
10-
"strings"
11-
"regexp"
12-
flags "github.com/jessevdk/go-flags"
4+
"fmt"
5+
"errors"
6+
"bytes"
7+
"io/ioutil"
8+
"bufio"
9+
"os"
10+
"strings"
11+
"regexp"
12+
flags "github.com/jessevdk/go-flags"
1313
)
1414

1515
const (
16-
Author = "webdevops.io"
17-
Version = "0.2.1"
16+
Author = "webdevops.io"
17+
Version = "0.2.1"
1818
)
1919

2020
var opts struct {
21-
Search string `short:"s" long:"search" required:"true" description:"search term"`
22-
SearchRegex *regexp.Regexp
23-
Replace string `short:"r" long:"replace" required:"true" description:"replacement term" `
24-
IgnoreCase bool `short:"i" long:"ignore-case" description:"ignore pattern case"`
25-
ReplaceLine bool ` long:"replace-line" description:"replace whole line instead of only match"`
26-
Regex bool ` long:"regex" description:"treat pattern as regex"`
27-
RegexBackref bool ` long:"regex-backrefs" description:"enable backreferences in replace term"`
28-
Verbose bool `short:"v" long:"verbose" description:"verbose mode"`
29-
DryRun bool ` long:"dry-run" description:"dry run mode"`
30-
ShowVersion bool `short:"V" long:"version" description:"show version and exit"`
31-
ShowHelp bool `short:"h" long:"help" description:"show this help message"`
21+
Search string `short:"s" long:"search" required:"true" description:"search term"`
22+
SearchRegex *regexp.Regexp
23+
Replace string `short:"r" long:"replace" required:"true" description:"replacement term" `
24+
IgnoreCase bool `short:"i" long:"ignore-case" description:"ignore pattern case"`
25+
ReplaceLine bool ` long:"replace-line" description:"replace whole line instead of only match"`
26+
Regex bool ` long:"regex" description:"treat pattern as regex"`
27+
RegexBackref bool ` long:"regex-backrefs" description:"enable backreferences in replace term"`
28+
RegexPosix bool ` long:"regex-posix" description:"parse regex term as POSIX regex"`
29+
Verbose bool `short:"v" long:"verbose" description:"verbose mode"`
30+
DryRun bool ` long:"dry-run" description:"dry run mode"`
31+
ShowVersion bool `short:"V" long:"version" description:"show version and exit"`
32+
ShowHelp bool `short:"h" long:"help" description:"show this help message"`
3233
}
3334

3435
// Replace line (if match is found) in file
3536
func replaceInFile(filepath string) {
37+
// try open file
3638
file, err := os.Open(filepath)
3739
if err != nil {
3840
panic(err)
@@ -45,9 +47,12 @@ func replaceInFile(filepath string) {
4547
line, e := Readln(r)
4648
for e == nil {
4749
if searchMatch(line) {
50+
// --replace-line
4851
if opts.ReplaceLine {
52+
// replace whole line with replace term
4953
line = opts.Replace
5054
} else {
55+
// replace only term inside line
5156
line = replaceText(line)
5257
}
5358

@@ -95,6 +100,7 @@ func searchMatch(content string) (bool) {
95100

96101
// Replace text in whole content based on search options
97102
func replaceText(content string) (string) {
103+
// --regex-backrefs
98104
if opts.RegexBackref {
99105
return opts.SearchRegex.ReplaceAllString(content, opts.Replace)
100106
} else {
@@ -104,14 +110,15 @@ func replaceText(content string) (string) {
104110

105111
// Write content to file
106112
func writeContentToFile(filepath string, content bytes.Buffer) {
113+
// --dry-run
107114
if opts.DryRun {
108115
title := fmt.Sprintf("%s:", filepath)
109116

117+
fmt.Println()
110118
fmt.Println(title)
111119
fmt.Println(strings.Repeat("-", len(title)))
112120
fmt.Println(content.String())
113121
fmt.Println()
114-
fmt.Println()
115122
} else {
116123
var err error
117124
err = ioutil.WriteFile(filepath, content.Bytes(), 0)
@@ -131,6 +138,7 @@ func logMessage(message string) {
131138
}
132139
}
133140

141+
// Log error object as message
134142
func logError(err error) {
135143
fmt.Printf("Error: %s\n", err)
136144
}
@@ -140,13 +148,16 @@ func logError(err error) {
140148
func buildSearchTerm() {
141149
var regex string
142150

151+
// --regex
143152
if opts.Regex {
153+
// use search term as regex
144154
regex = opts.Search
145155
} else {
156+
// use search term as normal string, escape it for regex usage
146157
regex = regexp.QuoteMeta(opts.Search)
147158
}
148159

149-
160+
// --ignore-case
150161
if opts.IgnoreCase {
151162
regex = "(?i:" + regex + ")"
152163
}
@@ -155,45 +166,50 @@ func buildSearchTerm() {
155166
logMessage(fmt.Sprintf("Using regular expression: %s", regex))
156167
}
157168

158-
opts.SearchRegex = regexp.MustCompile(regex)
169+
if opts.RegexPosix {
170+
opts.SearchRegex = regexp.MustCompilePOSIX(regex)
171+
} else {
172+
opts.SearchRegex = regexp.MustCompile(regex)
173+
}
159174
}
160175

161-
func handleSpecialOptions(argparser *flags.Parser, args []string) {
176+
func handleSpecialCliOptions(argparser *flags.Parser, args []string) {
177+
// --version
162178
if (opts.ShowVersion) {
163-
fmt.Printf("goreplace %s\n", Version)
179+
fmt.Printf("goreplace version %s\n", Version)
164180
os.Exit(0)
165181
}
166182

183+
// --help
167184
if (opts.ShowHelp) {
168-
argparser.WriteHelp(os.Stdout)
169-
os.Exit(1)
170-
}
171-
172-
if (len(args) == 0) {
173-
err := errors.New("No files specified")
174-
logError(err)
175-
fmt.Println()
176-
argparser.WriteHelp(os.Stdout)
177-
os.Exit(1)
178-
}
185+
argparser.WriteHelp(os.Stdout)
186+
os.Exit(1)
187+
}
188+
189+
// missing any files
190+
if (len(args) == 0) {
191+
err := errors.New("No files specified")
192+
logError(err)
193+
fmt.Println()
194+
argparser.WriteHelp(os.Stdout)
195+
os.Exit(1)
196+
}
179197
}
180198

181199
func main() {
182200
var argparser = flags.NewParser(&opts, flags.PassDoubleDash)
183-
args, err := argparser.Parse()
201+
args, err := argparser.Parse()
184202

185-
if err != nil {
186-
handleSpecialOptions(argparser, args)
203+
handleSpecialCliOptions(argparser, args)
187204

205+
if err != nil {
188206
logError(err)
189207
fmt.Println()
190208
argparser.WriteHelp(os.Stdout)
191209
os.Exit(1)
192210
}
193211

194-
handleSpecialOptions(argparser, args)
195-
196-
buildSearchTerm()
212+
buildSearchTerm()
197213

198214
for i := range args {
199215
var file string
@@ -203,4 +219,4 @@ func main() {
203219
}
204220

205221
os.Exit(0)
206-
}
222+
}

0 commit comments

Comments
 (0)