diff --git a/README.md b/README.md index cfab916..93059e4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # pounce -simple find and replace in files + +simple find and replace in files. + +``` +$ pounce collect -r -s 'dog' > /tmp/dogs.txt +$ cat /tmp/dogs.txt +README.md:1:I like dogs +some/dir/dogs.txt:3:dogs are the best! +$ # The idea is that this can be done interactively using vim. +$ # This allows trial and error and faster feedback, but for +$ # the sake of demo, we'll use sed. +$ sed -i '' -e 's/dog/cat/g' /tmp/dogs.txt +$ pounce apply < /tmp/dogs.txt +$ cat README.md +I like cats +``` + +One liner version: +``` +$ pounce collect -r -s 'dog' | sed -e 's/dog/cat/g' | pounce apply +``` + +# TODO + +[ ] deal with colons in file names. +[ ] deal with no EOL last line. +[ ] apply: only generate backup files if content actually changed. +[ ] apply: read/write piece wise. +[ ] \n vs \r\n? + +# Disclaimer + +Both cats and dogs are awesome. diff --git a/app.go b/app.go index 1ab1abe..493b18b 100644 --- a/app.go +++ b/app.go @@ -7,6 +7,6 @@ import ( var app = cli.App{ Commands: []*cli.Command{ &collectCmd, - &replaceCmd, + &applyCmd, }, } diff --git a/replace.go b/apply.go similarity index 81% rename from replace.go rename to apply.go index 513421b..313f1ef 100644 --- a/replace.go +++ b/apply.go @@ -11,27 +11,27 @@ import ( "github.com/urfave/cli/v2" ) -var replaceFlags = struct { +var applyFlags = struct { print bool bak string }{} -var replaceCmd = cli.Command{ - Name: "replace", - UsageText: "pounce replace [-opts]", - Description: "replace lines in files according given replacement content", - Aliases: []string{"r"}, +var applyCmd = cli.Command{ + Name: "apply", + UsageText: "pounce apply [-opts]", + Description: "apply modified modified lines", + Aliases: []string{"a"}, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "print", Aliases: []string{"p"}, - Destination: &replaceFlags.print, + Destination: &applyFlags.print, Usage: "print each incoming line", }, &cli.StringFlag{ Name: "bak", Aliases: []string{"i"}, - Destination: &replaceFlags.bak, + Destination: &applyFlags.bak, Usage: "if not empty, backup originals with given suffix", }, }, @@ -55,7 +55,7 @@ func processReplaceInput(r io.Reader) error { return nil } - if err := replace(acc.path, acc.data); err != nil { + if err := apply(acc.path, acc.data); err != nil { return fmt.Errorf("%s: %w", acc.path, err) } @@ -72,7 +72,7 @@ func processReplaceInput(r io.Reader) error { continue } - if replaceFlags.print { + if applyFlags.print { fmt.Fprintln(os.Stderr, text) } @@ -106,13 +106,14 @@ func processReplaceInput(r io.Reader) error { return nil } -func replace(path string, data map[int]string) error { +// TODO: all contents is read into memory. Need to do it piecewise. +func apply(path string, data map[int]string) error { content, err := os.ReadFile(path) if err != nil { return fmt.Errorf("%s: %w", path, err) } - if bak := replaceFlags.bak; len(bak) > 0 { + if bak := applyFlags.bak; len(bak) > 0 { if err := os.WriteFile(path+bak, content, 0755); err != nil { return fmt.Errorf("backup %s%s: %w", path, bak, err) } diff --git a/collect.go b/collect.go index 8df9552..7c71811 100644 --- a/collect.go +++ b/collect.go @@ -23,6 +23,7 @@ var collectFlags = struct { print bool bin bool recursive bool + abs bool }{} var collectCmd = cli.Command{ @@ -66,6 +67,12 @@ var collectCmd = cli.Command{ Destination: &collectFlags.bin, Usage: "also search in binary files", }, + &cli.BoolFlag{ + Name: "abs", + Aliases: []string{"a"}, + Destination: &collectFlags.abs, + Usage: "write all file's absolute paths", + }, }, Action: func(c *cli.Context) error { m, err := matcher() @@ -176,15 +183,23 @@ func gather(prev, path string, matcher func(string) bool) error { r = io.MultiReader(bytes.NewReader(pre), f) } + if collectFlags.abs { + apath, err := filepath.Abs(path) + if err != nil { + return fmt.Errorf("abs %s: %w", path, err) + } + + path = apath + } + if collectFlags.print { fmt.Fprintln(os.Stderr, path) } s := bufio.NewScanner(r) for lineNum := 1; s.Scan(); lineNum++ { - text := s.Text() - if matcher(text) { + if text := s.Text(); matcher(text) { fmt.Printf("%s:%d:%s\n", path, lineNum, text) } }