From 3e15a594b765927895f5472e119e9b46610487a3 Mon Sep 17 00:00:00 2001 From: Loren Gordon Date: Mon, 24 Apr 2023 04:01:53 -0700 Subject: [PATCH] cat: adds --separator option to cat command When using `rclone cat` to print the contents of several files, the user may want to inject some separator between the files, such as a comma or a newline. This patch adds a `--separator` option to the `cat` command to make that possible. The default value remains an empty string, `""`, maintaining the prior behavior of `rclone cat`. Closes #6968 --- cmd/bisync/bisync_test.go | 3 ++- cmd/cat/cat.go | 26 ++++++++++++++++++++------ docs/content/commands/rclone_cat.md | 25 +++++++++++++++++++------ fs/operations/operations.go | 9 ++++++++- fs/operations/operations_test.go | 24 +++++++++++++----------- 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/cmd/bisync/bisync_test.go b/cmd/bisync/bisync_test.go index 11299f012..7e419c4b9 100644 --- a/cmd/bisync/bisync_test.go +++ b/cmd/bisync/bisync_test.go @@ -824,8 +824,9 @@ func touchFiles(ctx context.Context, dateStr string, f fs.Fs, dir, glob string) err = nil buf := new(bytes.Buffer) size := obj.Size() + separator := "" if size > 0 { - err = operations.Cat(ctx, f, buf, 0, size) + err = operations.Cat(ctx, f, buf, 0, size, []byte(separator)) } info := object.NewStaticObjectInfo(remote, date, size, true, nil, f) if err == nil { diff --git a/cmd/cat/cat.go b/cmd/cat/cat.go index 03ed4e6e3..b92f3efbb 100644 --- a/cmd/cat/cat.go +++ b/cmd/cat/cat.go @@ -16,11 +16,12 @@ import ( // Globals var ( - head = int64(0) - tail = int64(0) - offset = int64(0) - count = int64(-1) - discard = false + head = int64(0) + tail = int64(0) + offset = int64(0) + count = int64(-1) + discard = false + separator = string("") ) func init() { @@ -31,6 +32,7 @@ func init() { flags.Int64VarP(cmdFlags, &offset, "offset", "", offset, "Start printing at offset N (or from end if -ve)") flags.Int64VarP(cmdFlags, &count, "count", "", count, "Only print N characters") flags.BoolVarP(cmdFlags, &discard, "discard", "", discard, "Discard the output instead of printing") + flags.StringVarP(cmdFlags, &separator, "separator", "", separator, "Separator to use between objects when printing multiple files") } var commandDefinition = &cobra.Command{ @@ -56,6 +58,18 @@ Use the |--head| flag to print characters only at the start, |--tail| for the end and |--offset| and |--count| to print a section in the middle. Note that if offset is negative it will count from the end, so |--offset -1 --count 1| is equivalent to |--tail 1|. + +Use the |--separator| flag to print a separator value between files. Be sure to +shell-escape special characters. For example, to print a newline between +files, use: + +* bash: + + rclone --include "*.txt" --separator $'\n' cat remote:path/to/dir + +* powershell: + + rclone --include "*.txt" --separator "|n" cat remote:path/to/dir `, "|", "`"), Annotations: map[string]string{ "versionIntroduced": "v1.33", @@ -82,7 +96,7 @@ Note that if offset is negative it will count from the end, so w = io.Discard } cmd.Run(false, false, command, func() error { - return operations.Cat(context.Background(), fsrc, w, offset, count) + return operations.Cat(context.Background(), fsrc, w, offset, count, []byte(separator)) }) }, } diff --git a/docs/content/commands/rclone_cat.md b/docs/content/commands/rclone_cat.md index 6ad41423d..2e6ec3869 100644 --- a/docs/content/commands/rclone_cat.md +++ b/docs/content/commands/rclone_cat.md @@ -32,6 +32,18 @@ the end and `--offset` and `--count` to print a section in the middle. Note that if offset is negative it will count from the end, so `--offset -1 --count 1` is equivalent to `--tail 1`. +Use the `--separator` flag to print a separator value between files. Be sure to +shell-escape special characters. For example, to print a newline between +files, use: + +* bash: + + rclone --include "*.txt" --separator $'\n' cat remote:path/to/dir + +* powershell: + + rclone --include "*.txt" --separator "`n" cat remote:path/to/dir + ``` rclone cat remote:path [flags] @@ -40,12 +52,13 @@ rclone cat remote:path [flags] ## Options ``` - --count int Only print N characters (default -1) - --discard Discard the output instead of printing - --head int Only print the first N characters - -h, --help help for cat - --offset int Start printing at offset N (or from end if -ve) - --tail int Only print the last N characters + --count int Only print N characters (default -1) + --discard Discard the output instead of printing + --head int Only print the first N characters + -h, --help help for cat + --offset int Start printing at offset N (or from end if -ve) + --separator string Separator to use between objects when printing multiple files + --tail int Only print the last N characters ``` See the [global flags page](/flags/) for global options not listed here. diff --git a/fs/operations/operations.go b/fs/operations/operations.go index 3f6fb6e73..2752b06c3 100644 --- a/fs/operations/operations.go +++ b/fs/operations/operations.go @@ -1259,7 +1259,7 @@ type readCloser struct { // // if count < 0 then it will be ignored // if count >= 0 then only that many characters will be output -func Cat(ctx context.Context, f fs.Fs, w io.Writer, offset, count int64) error { +func Cat(ctx context.Context, f fs.Fs, w io.Writer, offset, count int64, sep []byte) error { var mu sync.Mutex ci := fs.GetConfig(ctx) return ListFn(ctx, f, func(o fs.Object) { @@ -1301,6 +1301,13 @@ func Cat(ctx context.Context, f fs.Fs, w io.Writer, offset, count int64) error { err = fs.CountError(err) fs.Errorf(o, "Failed to send to output: %v", err) } + if len(sep) >= 0 { + _, err = w.Write(sep) + if err != nil { + err = fs.CountError(err) + fs.Errorf(o, "Failed to send separator to output: %v", err) + } + } }) } diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go index 45a2259be..5125e5865 100644 --- a/fs/operations/operations_test.go +++ b/fs/operations/operations_test.go @@ -529,23 +529,25 @@ func TestCat(t *testing.T) { r.CheckRemoteItems(t, file1, file2) for _, test := range []struct { - offset int64 - count int64 - a string - b string + offset int64 + count int64 + separator string + a string + b string }{ - {0, -1, "ABCDEFGHIJ", "012345678"}, - {0, 5, "ABCDE", "01234"}, - {-3, -1, "HIJ", "678"}, - {1, 3, "BCD", "123"}, + {0, -1, "", "ABCDEFGHIJ", "012345678"}, + {0, 5, "", "ABCDE", "01234"}, + {-3, -1, "", "HIJ", "678"}, + {1, 3, "", "BCD", "123"}, + {0, -1, "\n", "ABCDEFGHIJ", "012345678"}, } { var buf bytes.Buffer - err := operations.Cat(ctx, r.Fremote, &buf, test.offset, test.count) + err := operations.Cat(ctx, r.Fremote, &buf, test.offset, test.count, []byte(test.separator)) require.NoError(t, err) res := buf.String() - if res != test.a+test.b && res != test.b+test.a { - t.Errorf("Incorrect output from Cat(%d,%d): %q", test.offset, test.count, res) + if res != test.a+test.separator+test.b+test.separator && res != test.b+test.separator+test.a+test.separator { + t.Errorf("Incorrect output from Cat(%d,%d,%s): %q", test.offset, test.count, test.separator, res) } } }