diff --git a/changelog/unreleased/issue-2281 b/changelog/unreleased/issue-2281 new file mode 100644 index 000000000..3f442f206 --- /dev/null +++ b/changelog/unreleased/issue-2281 @@ -0,0 +1,7 @@ +Bugfix: Handle format verbs like '%' properly in `find` output + +The JSON or "normal" output of the `find` command can now deal with file names +that contain substrings which the Golang `fmt` package considers "format verbs" +like `%s`. + +https://github.com/restic/restic/issues/2281 diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index d11b9f3db..cbd005fe7 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -156,7 +156,7 @@ func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) { if s.hits > 0 { Printf(",") } - Printf(string(b)) + Print(string(b)) s.hits++ } @@ -168,7 +168,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *restic.Node) { s.oldsn = s.newsn Verbosef("Found matching entries in snapshot %s from %s\n", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(TimeFormat)) } - Printf(formatNode(path, node, s.ListLong) + "\n") + Println(formatNode(path, node, s.ListLong)) } func (s *statefulOutput) PrintPattern(path string, node *restic.Node) { @@ -207,7 +207,7 @@ func (s *statefulOutput) PrintObjectJSON(kind, id, nodepath, treeID string, sn * if s.hits > 0 { Printf(",") } - Printf(string(b)) + Print(string(b)) s.hits++ } diff --git a/cmd/restic/global.go b/cmd/restic/global.go index bfb8ba9e8..6a46b57ee 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -189,6 +189,22 @@ func Printf(format string, args ...interface{}) { } } +// Print writes the message to the configured stdout stream. +func Print(args ...interface{}) { + _, err := fmt.Fprint(globalOptions.stdout, args...) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to write to stdout: %v\n", err) + } +} + +// Println writes the message to the configured stdout stream. +func Println(args ...interface{}) { + _, err := fmt.Fprintln(globalOptions.stdout, args...) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to write to stdout: %v\n", err) + } +} + // Verbosef calls Printf to write the message when the verbose flag is set. func Verbosef(format string, args ...interface{}) { if globalOptions.verbosity >= 1 { diff --git a/cmd/restic/global_test.go b/cmd/restic/global_test.go new file mode 100644 index 000000000..7d0477be6 --- /dev/null +++ b/cmd/restic/global_test.go @@ -0,0 +1,28 @@ +package main + +import ( + "bytes" + "testing" + + rtest "github.com/restic/restic/internal/test" +) + +func Test_PrintFunctionsRespectsGlobalStdout(t *testing.T) { + gopts := globalOptions + defer func() { + globalOptions = gopts + }() + + buf := bytes.NewBuffer(nil) + globalOptions.stdout = buf + + for _, p := range []func(){ + func() { Println("message") }, + func() { Print("message\n") }, + func() { Printf("mes%s\n", "sage") }, + } { + p() + rtest.Equals(t, "message\n", buf.String()) + buf.Reset() + } +}