@@ -16,7 +16,7 @@ import (
1616
1717const (
1818 Author = "webdevops.io"
19- Version = "0.4 .0"
19+ Version = "0.5 .0"
2020)
2121
2222type changeset struct {
@@ -44,6 +44,7 @@ var opts struct {
4444 Search []string `short:"s" long:"search" required:"true" description:"search term"`
4545 Replace []string `short:"r" long:"replace" required:"true" description:"replacement term" `
4646 CaseInsensitive bool `short:"i" long:"case-insensitive" description:"ignore case of pattern to match upper and lowercase characters"`
47+ Stdin bool ` long:"stdin" description:"process stdin as input"`
4748 Once bool ` long:"once" description:"replace search term only one in a file"`
4849 OnceRemoveMatch bool ` long:"once-remove-match" description:"replace search term only one in a file and also don't keep matching lines (for line and lineinfile mode)"`
4950 Regex bool ` long:"regex" description:"treat pattern as regex"`
@@ -81,40 +82,14 @@ func applyChangesetsToFile(fileitem fileitem, changesets []changeset) (string, b
8182 r := bufio .NewReader (file )
8283 line , e := Readln (r )
8384 for e == nil {
84- writeLine := true
85+ newLine , lineChanged , skipLine := applyChangesetsToLine ( line , changesets )
8586
86- for i := range changesets {
87- changeset := changesets [i ]
88-
89- // --once, only do changeset once if already applied to file
90- if opts .Once && changeset .MatchFound {
91- // --once-without-match, skip matching lines
92- if opts .OnceRemoveMatch && searchMatch (line , changeset ) {
93- // matching line, not writing to buffer as requsted
94- writeLine = false
95- writeBufferToFile = true
96- break
97- }
98- } else {
99- // search and replace
100- if searchMatch (line , changeset ) {
101- // --mode=line or --mode=lineinfile
102- if opts .ModeIsReplaceLine || opts .ModeIsLineInFile {
103- // replace whole line with replace term
104- line = changeset .Replace
105- } else {
106- // replace only term inside line
107- line = replaceText (line , changeset )
108- }
109-
110- changesets [i ].MatchFound = true
111- writeBufferToFile = true
112- }
113- }
87+ if lineChanged || skipLine {
88+ writeBufferToFile = true
11489 }
11590
116- if ( writeLine ) {
117- buffer .WriteString (line + "\n " )
91+ if ! skipLine {
92+ buffer .WriteString (newLine + "\n " )
11893 }
11994
12095 line , e = Readln (r )
@@ -140,6 +115,43 @@ func applyChangesetsToFile(fileitem fileitem, changesets []changeset) (string, b
140115 return output , status , err
141116}
142117
118+ func applyChangesetsToLine (line string , changesets []changeset ) (string , bool , bool ) {
119+ changed := false
120+ skipLine := false
121+
122+ for i := range changesets {
123+ changeset := changesets [i ]
124+
125+ // --once, only do changeset once if already applied to file
126+ if opts .Once && changeset .MatchFound {
127+ // --once-without-match, skip matching lines
128+ if opts .OnceRemoveMatch && searchMatch (line , changeset ) {
129+ // matching line, not writing to buffer as requsted
130+ skipLine = true
131+ changed = true
132+ break
133+ }
134+ } else {
135+ // search and replace
136+ if searchMatch (line , changeset ) {
137+ // --mode=line or --mode=lineinfile
138+ if opts .ModeIsReplaceLine || opts .ModeIsLineInFile {
139+ // replace whole line with replace term
140+ line = changeset .Replace
141+ } else {
142+ // replace only term inside line
143+ line = replaceText (line , changeset )
144+ }
145+
146+ changesets [i ].MatchFound = true
147+ changed = true
148+ }
149+ }
150+ }
151+
152+ return line , changed , skipLine
153+ }
154+
143155// Readln returns a single line (without the ending \n)
144156// from the input buffered reader.
145157// An error is returned iff there is an error with the
@@ -195,13 +207,13 @@ func writeContentToFile(fileitem fileitem, content bytes.Buffer) (string, bool)
195207// Log message
196208func logMessage (message string ) {
197209 if opts .Verbose {
198- fmt .Println ( message )
210+ fmt .Fprintln ( os . Stderr , message )
199211 }
200212}
201213
202214// Log error object as message
203215func logError (err error ) {
204- fmt .Printf ( "Error: %s\n " , err )
216+ fmt .Fprintln ( os . Stderr , "Error: %s\n " , err )
205217}
206218
207219// Build search term
@@ -343,43 +355,23 @@ func handleSpecialCliOptions(argparser *flags.Parser, args []string) ([]string)
343355 return args
344356}
345357
346- func main () {
347- var changesets = []changeset {}
358+ func actionProcessStdin (changesets []changeset ) (int ) {
359+ scanner := bufio .NewScanner (os .Stdin )
360+ for scanner .Scan () {
361+ line := scanner .Text ()
348362
349- var argparser = flags .NewParser (& opts , flags .PassDoubleDash )
350- args , err := argparser .Parse ()
363+ newLine , _ , skipLine := applyChangesetsToLine (line , changesets )
351364
352- args = handleSpecialCliOptions (argparser , args )
353-
354- // check if there is an parse error
355- if err != nil {
356- logError (err )
357- fmt .Println ()
358- argparser .WriteHelp (os .Stdout )
359- os .Exit (1 )
360- }
361-
362- // check if search and replace options have equal lenght (equal number of options)
363- if len (opts .Search ) != len (opts .Replace ) {
364- // error: unequal numbers of search and replace options
365- err := errors .New ("Unequal numbers of search or replace options" )
366- logError (err )
367- fmt .Println ()
368- argparser .WriteHelp (os .Stdout )
369- os .Exit (1 )
365+ if ! skipLine {
366+ fmt .Println (newLine )
367+ }
370368 }
371369
372- // build changesets
373- for i := range opts .Search {
374- search := opts .Search [i ]
375- replace := opts .Replace [i ]
376-
377- changeset := changeset {buildSearchTerm (search ), replace , false }
378-
379- changesets = append (changesets , changeset )
380- }
370+ return 0
371+ }
381372
382- // check if there is at least one file to process
373+ func actionProcessFiles (changesets []changeset , args []string , argparser * flags.Parser ) (int ) {
374+ // check if there is at least one file to process
383375 if (len (args ) == 0 ) {
384376 if (opts .IgnoreEmpty ) {
385377 // no files found, but we should ignore empty filelist
@@ -389,9 +381,9 @@ func main() {
389381 // no files found, print error and exit with error code
390382 err := errors .New ("No files specified" )
391383 logError (err )
392- fmt .Println ( )
384+ fmt .Fprintln ( os . Stderr , "" )
393385 argparser .WriteHelp (os .Stdout )
394- os . Exit ( 1 )
386+ return 1
395387 }
396388 }
397389
@@ -426,20 +418,72 @@ func main() {
426418 } else if opts .Verbose {
427419 title := fmt .Sprintf ("%s:" , result .File .Path )
428420
429- fmt .Println ( )
430- fmt .Println ( title )
431- fmt .Println ( strings .Repeat ("-" , len (title )))
432- fmt .Println ( )
433- fmt .Println ( result .Output )
434- fmt .Println ( )
421+ fmt .Fprintln ( os . Stderr , "" )
422+ fmt .Fprintln ( os . Stderr , title )
423+ fmt .Fprintln ( os . Stderr , strings .Repeat ("-" , len (title )))
424+ fmt .Fprintln ( os . Stderr , "" )
425+ fmt .Fprintln ( os . Stderr , result .Output )
426+ fmt .Fprintln ( os . Stderr , "" )
435427 }
436428 }
437429
438430 if errorCount >= 1 {
439- fmt .Println (fmt .Sprintf ("[ERROR] %s failed with %d error(s)" , argparser .Command .Name , errorCount ))
431+ fmt .Fprintln (os .Stderr , fmt .Sprintf ("[ERROR] %s failed with %d error(s)" , argparser .Command .Name , errorCount ))
432+ return 1
433+ }
434+
435+ return 0
436+ }
437+
438+ func buildChangesets (argparser * flags.Parser ) ([]changeset ){
439+ var changesets []changeset
440+
441+ // check if search and replace options have equal lenght (equal number of options)
442+ if len (opts .Search ) != len (opts .Replace ) {
443+ // error: unequal numbers of search and replace options
444+ err := errors .New ("Unequal numbers of search or replace options" )
445+ logError (err )
446+ fmt .Fprintln (os .Stderr , "" )
447+ argparser .WriteHelp (os .Stdout )
448+ os .Exit (1 )
449+ }
450+
451+ // build changesets
452+ for i := range opts .Search {
453+ search := opts .Search [i ]
454+ replace := opts .Replace [i ]
455+
456+ changeset := changeset {buildSearchTerm (search ), replace , false }
457+ changesets = append (changesets , changeset )
458+ }
459+
460+ return changesets
461+ }
462+
463+ func main () {
464+ var argparser = flags .NewParser (& opts , flags .PassDoubleDash )
465+ args , err := argparser .Parse ()
466+
467+ args = handleSpecialCliOptions (argparser , args )
468+
469+ // check if there is an parse error
470+ if err != nil {
471+ logError (err )
472+ fmt .Fprintln (os .Stderr , "" )
473+ argparser .WriteHelp (os .Stdout )
440474 os .Exit (1 )
475+ }
476+
477+ changesets := buildChangesets (argparser )
478+
479+ exitMode := 0
480+ if opts .Stdin {
481+ // use stdin as input
482+ exitMode = actionProcessStdin (changesets )
441483 } else {
442- os .Exit (0 )
484+ // use and process files (see args)
485+ exitMode = actionProcessFiles (changesets , args , argparser )
443486 }
444487
488+ os .Exit (exitMode )
445489}
0 commit comments