cmd: new command lsf
This commit is contained in:
parent
8f47d7fc06
commit
0d041602cf
11 changed files with 379 additions and 0 deletions
|
@ -25,6 +25,7 @@ import (
|
||||||
_ "github.com/ncw/rclone/cmd/ls"
|
_ "github.com/ncw/rclone/cmd/ls"
|
||||||
_ "github.com/ncw/rclone/cmd/ls2"
|
_ "github.com/ncw/rclone/cmd/ls2"
|
||||||
_ "github.com/ncw/rclone/cmd/lsd"
|
_ "github.com/ncw/rclone/cmd/lsd"
|
||||||
|
_ "github.com/ncw/rclone/cmd/lsf"
|
||||||
_ "github.com/ncw/rclone/cmd/lsjson"
|
_ "github.com/ncw/rclone/cmd/lsjson"
|
||||||
_ "github.com/ncw/rclone/cmd/lsl"
|
_ "github.com/ncw/rclone/cmd/lsl"
|
||||||
_ "github.com/ncw/rclone/cmd/md5sum"
|
_ "github.com/ncw/rclone/cmd/md5sum"
|
||||||
|
|
78
cmd/lsf/lsf.go
Normal file
78
cmd/lsf/lsf.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package lsf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/cmd"
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
format string
|
||||||
|
separator string
|
||||||
|
dirSlash bool
|
||||||
|
recurse bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmd.Root.AddCommand(commandDefintion)
|
||||||
|
flags := commandDefintion.Flags()
|
||||||
|
flags.StringVarP(&format, "format", "F", "", "Output format.")
|
||||||
|
flags.StringVarP(&separator, "separator", "s", "", "Separator.")
|
||||||
|
flags.BoolVarP(&dirSlash, "dir-slash", "d", false, "Dir name contains slash one the end.")
|
||||||
|
commandDefintion.Flags().BoolVarP(&recurse, "recursive", "R", false, "Recurse into the listing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandDefintion = &cobra.Command{
|
||||||
|
Use: "lsf remote:path",
|
||||||
|
Short: `List all the objects in the path with modification time, size and path in specific format: 'p' - path, 's' - size, 't' - modification time, ex. 'tsp'. Default output contains only path. If format is empty, dir-slash flag is always true.`,
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
cmd.CheckArgs(1, 1, command, args)
|
||||||
|
fsrc := cmd.NewFsSrc(args)
|
||||||
|
cmd.Run(false, false, command, func() error {
|
||||||
|
return Lsf(fsrc, os.Stdout)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//Lsf lists all the objects in the path with modification time, size and path in specific format.
|
||||||
|
func Lsf(fsrc fs.Fs, out io.Writer) error {
|
||||||
|
return fs.Walk(fsrc, "", false, fs.ConfigMaxDepth(recurse), func(path string, entries fs.DirEntries, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
fs.Stats.Error(err)
|
||||||
|
fs.Errorf(path, "error listing: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if format == "" {
|
||||||
|
format = "p"
|
||||||
|
dirSlash = true
|
||||||
|
}
|
||||||
|
if separator == "" {
|
||||||
|
separator = ";"
|
||||||
|
}
|
||||||
|
var list fs.ListFormat
|
||||||
|
list.SetSeparator(separator)
|
||||||
|
list.SetDirSlash(dirSlash)
|
||||||
|
|
||||||
|
for _, char := range format {
|
||||||
|
switch char {
|
||||||
|
case 'p':
|
||||||
|
list.AddPath()
|
||||||
|
case 't':
|
||||||
|
list.AddModTime()
|
||||||
|
case 's':
|
||||||
|
list.AddSize()
|
||||||
|
default:
|
||||||
|
return errors.Wrap(err, "failed to parse format argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
fmt.Fprintln(out, fs.ListFormatted(&entry, &list))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
194
cmd/lsf/lsf_test.go
Normal file
194
cmd/lsf/lsf_test.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package lsf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/fstest"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
_ "github.com/ncw/rclone/local"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultLsf(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1
|
||||||
|
file2
|
||||||
|
file3
|
||||||
|
subdir/
|
||||||
|
`, buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecurseFlag(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
recurse = true
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1
|
||||||
|
file2
|
||||||
|
file3
|
||||||
|
subdir/
|
||||||
|
subdir/file1
|
||||||
|
subdir/file2
|
||||||
|
subdir/file3
|
||||||
|
`, buf.String())
|
||||||
|
recurse = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDirSlashFlag(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dirSlash = true
|
||||||
|
format = "p"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1
|
||||||
|
file2
|
||||||
|
file3
|
||||||
|
subdir/
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
dirSlash = false
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1
|
||||||
|
file2
|
||||||
|
file3
|
||||||
|
subdir
|
||||||
|
`, buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormat(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
format = "p"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1
|
||||||
|
file2
|
||||||
|
file3
|
||||||
|
subdir
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
format = "s"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `0
|
||||||
|
321
|
||||||
|
1234
|
||||||
|
-1
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
format = "t"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
items, _ := fs.ListDirSorted(f, true, "")
|
||||||
|
var expectedOutput string
|
||||||
|
for _, item := range items {
|
||||||
|
expectedOutput += item.ModTime().Format("2006-01-02 15:04:05") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedOutput, buf.String())
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
format = "sp"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `0;file1
|
||||||
|
321;file2
|
||||||
|
1234;file3
|
||||||
|
-1;subdir
|
||||||
|
`, buf.String())
|
||||||
|
format = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeparator(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
format = "ps"
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1;0
|
||||||
|
file2;321
|
||||||
|
file3;1234
|
||||||
|
subdir;-1
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
separator = "__SEP__"
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `file1__SEP__0
|
||||||
|
file2__SEP__321
|
||||||
|
file3__SEP__1234
|
||||||
|
subdir__SEP__-1
|
||||||
|
`, buf.String())
|
||||||
|
format = ""
|
||||||
|
separator = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWholeLsf(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
f, err := fs.NewFs("testfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
format = "pst"
|
||||||
|
separator = "_+_"
|
||||||
|
recurse = true
|
||||||
|
dirSlash = true
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
items, _ := fs.ListDirSorted(f, true, "")
|
||||||
|
itemsInSubdir, _ := fs.ListDirSorted(f, true, "subdir")
|
||||||
|
var expectedOutput []string
|
||||||
|
for _, item := range items {
|
||||||
|
expectedOutput = append(expectedOutput, item.ModTime().Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
for _, item := range itemsInSubdir {
|
||||||
|
expectedOutput = append(expectedOutput, item.ModTime().Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, `file1_+_0_+_`+expectedOutput[0]+`
|
||||||
|
file2_+_321_+_`+expectedOutput[1]+`
|
||||||
|
file3_+_1234_+_`+expectedOutput[2]+`
|
||||||
|
subdir/_+_-1_+_`+expectedOutput[3]+`
|
||||||
|
subdir/file1_+_0_+_`+expectedOutput[4]+`
|
||||||
|
subdir/file2_+_1_+_`+expectedOutput[5]+`
|
||||||
|
subdir/file3_+_111_+_`+expectedOutput[6]+`
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
format = ""
|
||||||
|
separator = ""
|
||||||
|
recurse = false
|
||||||
|
dirSlash = false
|
||||||
|
}
|
0
cmd/lsf/testfiles/file1
Normal file
0
cmd/lsf/testfiles/file1
Normal file
BIN
cmd/lsf/testfiles/file2
Normal file
BIN
cmd/lsf/testfiles/file2
Normal file
Binary file not shown.
BIN
cmd/lsf/testfiles/file3
Normal file
BIN
cmd/lsf/testfiles/file3
Normal file
Binary file not shown.
0
cmd/lsf/testfiles/subdir/file1
Normal file
0
cmd/lsf/testfiles/subdir/file1
Normal file
BIN
cmd/lsf/testfiles/subdir/file2
Normal file
BIN
cmd/lsf/testfiles/subdir/file2
Normal file
Binary file not shown.
BIN
cmd/lsf/testfiles/subdir/file3
Normal file
BIN
cmd/lsf/testfiles/subdir/file3
Normal file
Binary file not shown.
|
@ -11,6 +11,7 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -1784,3 +1785,66 @@ func MoveFile(fdst Fs, fsrc Fs, dstFileName string, srcFileName string) (err err
|
||||||
func CopyFile(fdst Fs, fsrc Fs, dstFileName string, srcFileName string) (err error) {
|
func CopyFile(fdst Fs, fsrc Fs, dstFileName string, srcFileName string) (err error) {
|
||||||
return moveOrCopyFile(fdst, fsrc, dstFileName, srcFileName, true)
|
return moveOrCopyFile(fdst, fsrc, dstFileName, srcFileName, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListFormat defines files information print format
|
||||||
|
type ListFormat struct {
|
||||||
|
separator string
|
||||||
|
dirSlash bool
|
||||||
|
output []func() string
|
||||||
|
entry DirEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSeparator changes separator in struct
|
||||||
|
func (l *ListFormat) SetSeparator(separator string) {
|
||||||
|
l.separator = separator
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDirSlash defines if slash should be printed
|
||||||
|
func (l *ListFormat) SetDirSlash(dirSlash bool) {
|
||||||
|
l.dirSlash = dirSlash
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets functions used to create files information
|
||||||
|
func (l *ListFormat) SetOutput(output []func() string) {
|
||||||
|
l.output = output
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddModTime adds file's Mod Time to output
|
||||||
|
func (l *ListFormat) AddModTime() {
|
||||||
|
l.AppendOutput(func() string { return l.entry.ModTime().Format("2006-01-02 15:04:05") })
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSize adds file's size to output
|
||||||
|
func (l *ListFormat) AddSize() {
|
||||||
|
l.AppendOutput(func() string { return strconv.FormatInt(l.entry.Size(), 10) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPath adds path to file to output
|
||||||
|
func (l *ListFormat) AddPath() {
|
||||||
|
l.AppendOutput(func() string {
|
||||||
|
_, isDir := l.entry.(Directory)
|
||||||
|
|
||||||
|
if isDir && l.dirSlash {
|
||||||
|
return l.entry.Remote() + "/"
|
||||||
|
}
|
||||||
|
return l.entry.Remote()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendOutput adds string generated by specific function to printed output
|
||||||
|
func (l *ListFormat) AppendOutput(functionToAppend func() string) {
|
||||||
|
if len(l.output) > 0 {
|
||||||
|
l.output = append(l.output, func() string { return l.separator })
|
||||||
|
}
|
||||||
|
l.output = append(l.output, functionToAppend)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFormatted prints information about specific file in specific format
|
||||||
|
func ListFormatted(entry *DirEntry, list *ListFormat) string {
|
||||||
|
list.entry = *entry
|
||||||
|
var out string
|
||||||
|
for _, fun := range list.output {
|
||||||
|
out += fun()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
@ -958,3 +958,45 @@ func TestCheckEqualReaders(t *testing.T) {
|
||||||
assert.Equal(t, myErr, err)
|
assert.Equal(t, myErr, err)
|
||||||
assert.Equal(t, differ, true)
|
assert.Equal(t, differ, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListFormat(t *testing.T) {
|
||||||
|
r := fstest.NewRun(t)
|
||||||
|
defer r.Finalise()
|
||||||
|
file1 := r.WriteObject("a", "a", t1)
|
||||||
|
file2 := r.WriteObject("subdir/b", "b", t1)
|
||||||
|
|
||||||
|
fstest.CheckItems(t, r.Fremote, file1, file2)
|
||||||
|
|
||||||
|
items, _ := fs.ListDirSorted(r.Fremote, true, "")
|
||||||
|
var list fs.ListFormat
|
||||||
|
list.AddPath()
|
||||||
|
list.SetDirSlash(false)
|
||||||
|
assert.Equal(t, "subdir", fs.ListFormatted(&items[1], &list))
|
||||||
|
|
||||||
|
list.SetDirSlash(true)
|
||||||
|
assert.Equal(t, "subdir/", fs.ListFormatted(&items[1], &list))
|
||||||
|
|
||||||
|
list.SetOutput(nil)
|
||||||
|
assert.Equal(t, "", fs.ListFormatted(&items[1], &list))
|
||||||
|
|
||||||
|
list.AppendOutput(func() string { return "a" })
|
||||||
|
list.AppendOutput(func() string { return "b" })
|
||||||
|
assert.Equal(t, "ab", fs.ListFormatted(&items[1], &list))
|
||||||
|
list.SetSeparator(":::")
|
||||||
|
assert.Equal(t, "a:::b", fs.ListFormatted(&items[1], &list))
|
||||||
|
|
||||||
|
list.SetOutput(nil)
|
||||||
|
list.AddModTime()
|
||||||
|
assert.Equal(t, items[0].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[0], &list))
|
||||||
|
|
||||||
|
list.SetOutput(nil)
|
||||||
|
list.AddSize()
|
||||||
|
assert.Equal(t, "1", fs.ListFormatted(&items[0], &list))
|
||||||
|
|
||||||
|
list.AddPath()
|
||||||
|
list.AddModTime()
|
||||||
|
list.SetDirSlash(true)
|
||||||
|
list.SetSeparator("__SEP__")
|
||||||
|
assert.Equal(t, "1__SEP__a__SEP__"+items[0].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[0], &list))
|
||||||
|
assert.Equal(t, "-1__SEP__subdir/__SEP__"+items[1].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[1], &list))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue