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
This commit is contained in:
Loren Gordon 2023-04-24 04:01:53 -07:00 committed by GitHub
parent db8c007983
commit 3e15a594b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 25 deletions

View File

@ -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 {

View File

@ -21,6 +21,7 @@ var (
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))
})
},
}

View File

@ -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]
@ -45,6 +57,7 @@ rclone cat remote:path [flags]
--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
```

View File

@ -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)
}
}
})
}

View File

@ -531,21 +531,23 @@ func TestCat(t *testing.T) {
for _, test := range []struct {
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)
}
}
}