Skip to content

Commit 1a15218

Browse files
committed
Add --lineinfile-before and --lineinfile-after
1 parent 7323e4e commit 1a15218

File tree

5 files changed

+246
-129
lines changed

5 files changed

+246
-129
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ Usage:
3030
go-replace
3131
3232
Application Options:
33-
-m, --mode=[replace|line|lineinfile|template] replacement mode - replace: replace match with term; line: replace line with term; lineinfile: replace line with
34-
term or if not found append to term to file; template: parse content as golang template, search value have to start
35-
uppercase (default: replace)
33+
-m, --mode=[replace|line|lineinfile|template] replacement mode - replace: replace match with term; line: replace line with term;
34+
lineinfile: replace line with term or if not found append to term to file; template: parse
35+
content as golang template, search value have to start uppercase (default: replace)
3636
-s, --search= search term
3737
-r, --replace= replacement term
38+
--lineinfile-before= add line before this regex
39+
--lineinfile-after= add line after this regex
3840
-i, --case-insensitive ignore case of pattern to match upper and lowercase characters
3941
--stdin process stdin as input
4042
-o, --output= write changes to this file (in one file mode)
4143
--output-strip-ext= strip file extension from written files (also available in multi file mode)
42-
--once=[keep|unique] replace search term only one in a file, keep duplicaes (keep, default) or remove them (unique)
44+
--once=[keep|unique] replace search term only one in a file, keep duplicaes (keep, default) or remove them
45+
(unique)
4346
--regex treat pattern as regex
4447
--regex-backrefs enable backreferences in replace term
4548
--regex-posix parse regex term as POSIX regex

funcs.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ package main
22

33
import (
44
)
5+
import (
6+
"regexp"
7+
"bytes"
8+
"bufio"
9+
)
510

611
// check if string is contained in an array
712
func contains(slice []string, item string) bool {
@@ -32,3 +37,65 @@ func replaceText(content string, changeset changeset) (string) {
3237
return changeset.Search.ReplaceAllLiteralString(content, changeset.Replace)
3338
}
3439
}
40+
41+
func handleLineInFile(changesets []changeset, buffer bytes.Buffer) (*bytes.Buffer, bool) {
42+
var (
43+
line string
44+
writeBufferToFile bool
45+
)
46+
47+
for _, changeset := range changesets {
48+
if !changeset.MatchFound {
49+
// just add line to file
50+
line = changeset.Replace + "\n"
51+
52+
// remove backrefs (no match)
53+
if opts.RegexBackref {
54+
line = regexp.MustCompile("\\$[0-9]+").ReplaceAllLiteralString(line, "")
55+
}
56+
57+
// --lineinfile-before
58+
// --lineinfile-after
59+
if opts.LineinfileBefore != "" || opts.LineinfileAfter != "" {
60+
var matchFinder *regexp.Regexp
61+
62+
if opts.LineinfileBefore != "" {
63+
matchFinder = regexp.MustCompile(opts.LineinfileBefore)
64+
} else {
65+
matchFinder = regexp.MustCompile(opts.LineinfileAfter)
66+
}
67+
68+
var bufferCopy bytes.Buffer
69+
70+
scanner := bufio.NewScanner(&buffer)
71+
for scanner.Scan() {
72+
originalLine := scanner.Text()
73+
74+
if matchFinder.MatchString(originalLine) {
75+
writeBufferToFile = true
76+
77+
if opts.LineinfileBefore != "" {
78+
bufferCopy.WriteString(line)
79+
}
80+
81+
bufferCopy.WriteString(originalLine + "\n")
82+
83+
if opts.LineinfileAfter != "" {
84+
bufferCopy.WriteString(line)
85+
}
86+
} else {
87+
bufferCopy.WriteString(originalLine + "\n")
88+
}
89+
}
90+
91+
buffer.Reset()
92+
buffer.WriteString(bufferCopy.String())
93+
} else {
94+
buffer.WriteString(line)
95+
writeBufferToFile = true
96+
}
97+
}
98+
}
99+
100+
return &buffer, writeBufferToFile
101+
}

main.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ var opts struct {
4444
ModeIsLineInFile bool
4545
ModeIsTemplate bool
4646
Search []string `short:"s" long:"search" description:"search term"`
47-
Replace []string `short:"r" long:"replace" description:"replacement term" `
47+
Replace []string `short:"r" long:"replace" description:"replacement term"`
48+
LineinfileBefore string ` long:"lineinfile-before" description:"add line before this regex"`
49+
LineinfileAfter string ` long:"lineinfile-after" description:"add line after this regex"`
4850
CaseInsensitive bool `short:"i" long:"case-insensitive" description:"ignore case of pattern to match upper and lowercase characters"`
4951
Stdin bool ` long:"stdin" description:"process stdin as input"`
5052
Output string `short:"o" long:"output" description:"write changes to this file (in one file mode)"`
@@ -101,18 +103,11 @@ func applyChangesetsToFile(fileitem fileitem, changesets []changeset) (string, b
101103

102104
// --mode=lineinfile
103105
if opts.ModeIsLineInFile {
104-
for _, changeset := range changesets {
105-
if !changeset.MatchFound {
106-
line = changeset.Replace + "\n"
107-
108-
// remove backrefs (no match)
109-
if opts.RegexBackref {
110-
line = regexp.MustCompile("\\$[0-9]+").ReplaceAllLiteralString(line, "")
111-
}
112-
113-
buffer.WriteString(line)
114-
writeBufferToFile = true
115-
}
106+
lifBuffer, lifStatus := handleLineInFile(changesets, buffer)
107+
if lifStatus {
108+
buffer.Reset()
109+
buffer.WriteString(lifBuffer.String())
110+
writeBufferToFile = lifStatus
116111
}
117112
}
118113

@@ -295,6 +290,16 @@ func handleSpecialCliOptions(args []string) ([]string) {
295290
logFatalErrorAndExit(errors.New("Only one file is allowed when using --output"), 1)
296291
}
297292

293+
if opts.LineinfileBefore != "" || opts.LineinfileAfter != "" {
294+
if ! opts.ModeIsLineInFile {
295+
logFatalErrorAndExit(errors.New("--lineinfile-after and --lineinfile-before only valid in --mode=lineinfile"), 1)
296+
}
297+
298+
if opts.LineinfileBefore != "" && opts.LineinfileAfter != "" {
299+
logFatalErrorAndExit(errors.New("Only --lineinfile-after or --lineinfile-before is allowed in --mode=lineinfile"), 1)
300+
}
301+
}
302+
298303
return args
299304
}
300305

tests/lineinfile.test

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
Go Lineinfile tests:
2+
3+
$ CURRENT="$(pwd)"
4+
$ cd "$TESTDIR/../"
5+
$ go build -o goreplace
6+
$ cd "$CURRENT"
7+
$ alias goreplace="$TESTDIR/../goreplace"
8+
9+
Exec test:
10+
11+
$ goreplace -h > /dev/null
12+
[1]
13+
14+
15+
Testing lineinfile mode:
16+
17+
$ cat > test.txt <<EOF
18+
> this is a testline
19+
> this is the second line
20+
> this is the third foobar line
21+
> this is the last line
22+
> EOF
23+
$ goreplace --mode=lineinfile -s foobar -r ___xxx test.txt
24+
$ cat test.txt
25+
this is a testline
26+
this is the second line
27+
___xxx
28+
this is the last line
29+
30+
Testing lineinfile mode with multiple matches:
31+
32+
$ cat > test.txt <<EOF
33+
> this is a testline
34+
> this is the second line
35+
> this is the third foobar line
36+
> this is the foobar forth foobar line
37+
> this is the last line
38+
> EOF
39+
$ goreplace --mode=lineinfile -s foobar -r ___xxx test.txt
40+
$ cat test.txt
41+
this is a testline
42+
this is the second line
43+
___xxx
44+
___xxx
45+
this is the last line
46+
47+
Testing lineinfile mode with multiple matches and --once:
48+
49+
$ cat > test.txt <<EOF
50+
> this is a testline
51+
> this is the second line
52+
> this is the third foobar line
53+
> this is the foobar forth foobar line
54+
> this is the last line
55+
> EOF
56+
$ goreplace --mode=lineinfile -s foobar -r ___xxx --once test.txt
57+
$ cat test.txt
58+
this is a testline
59+
this is the second line
60+
___xxx
61+
this is the foobar forth foobar line
62+
this is the last line
63+
64+
Testing lineinfile mode with multiple matches and --once=unique:
65+
66+
$ cat > test.txt <<EOF
67+
> this is a testline
68+
> this is the second line
69+
> this is the third foobar line
70+
> this is the foobar forth foobar line
71+
> this is the last line
72+
> EOF
73+
$ goreplace --mode=lineinfile -s foobar -r ___xxx --once=unique test.txt
74+
$ cat test.txt
75+
this is a testline
76+
this is the second line
77+
___xxx
78+
this is the last line
79+
80+
Testing lineinfile mode without match:
81+
82+
$ cat > test.txt <<EOF
83+
> this is a testline
84+
> this is the second line
85+
> this is the third foobar line
86+
> this is the foobar forth foobar line
87+
> this is the last line
88+
> EOF
89+
$ goreplace --mode=lineinfile -s barfoo -r ___xxx --once=unique test.txt
90+
$ cat test.txt
91+
this is a testline
92+
this is the second line
93+
this is the third foobar line
94+
this is the foobar forth foobar line
95+
this is the last line
96+
___xxx
97+
98+
Testing lineinfile mode with regex:
99+
100+
$ cat > test.txt <<EOF
101+
> this is a testline
102+
> this is the second line
103+
> this is the third foobar line
104+
> this is the last line
105+
> EOF
106+
$ goreplace --mode=lineinfile --regex --regex-backrefs -s 'f[o]+(b[a]*r)' -r '___$1' test.txt
107+
$ cat test.txt
108+
this is a testline
109+
this is the second line
110+
___bar
111+
this is the last line
112+
$ goreplace --mode=lineinfile --regex --regex-backrefs -s 'not-existing-line' -r '___$1' test.txt
113+
$ cat test.txt
114+
this is a testline
115+
this is the second line
116+
___bar
117+
this is the last line
118+
___
119+
120+
Testing lineinfile mode with lineinfile-before:
121+
122+
$ cat > test.txt <<EOF
123+
> this is a testline
124+
> #global#
125+
> this is the second line
126+
> this is the third foobar line
127+
> this is the last line
128+
> EOF
129+
$ goreplace --mode=lineinfile --lineinfile-before="#global#" -s 'notexisting' -r 'example=foobar' test.txt
130+
$ cat test.txt
131+
this is a testline
132+
example=foobar
133+
#global#
134+
this is the second line
135+
this is the third foobar line
136+
this is the last line
137+
138+
Testing lineinfile mode with lineinfile-after:
139+
140+
$ cat > test.txt <<EOF
141+
> this is a testline
142+
> #global#
143+
> this is the second line
144+
> this is the third foobar line
145+
> this is the last line
146+
> EOF
147+
$ goreplace --mode=lineinfile --lineinfile-after="#global#" -s 'notexisting' -r 'example=foobar' test.txt
148+
$ cat test.txt
149+
this is a testline
150+
#global#
151+
example=foobar
152+
this is the second line
153+
this is the third foobar line
154+
this is the last line

0 commit comments

Comments
 (0)