mirror of
https://github.com/rclone/rclone
synced 2024-12-27 19:43:48 +01:00
lsf: add option to print hashes
This commit is contained in:
parent
7c71ee1a5b
commit
c74c3b37da
@ -17,6 +17,7 @@ var (
|
|||||||
separator string
|
separator string
|
||||||
dirSlash bool
|
dirSlash bool
|
||||||
recurse bool
|
recurse bool
|
||||||
|
hashType = fs.HashMD5
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -25,6 +26,7 @@ func init() {
|
|||||||
flags.StringVarP(&format, "format", "F", "p", "Output format - see help for details")
|
flags.StringVarP(&format, "format", "F", "p", "Output format - see help for details")
|
||||||
flags.StringVarP(&separator, "separator", "s", ";", "Separator for the items in the format.")
|
flags.StringVarP(&separator, "separator", "s", ";", "Separator for the items in the format.")
|
||||||
flags.BoolVarP(&dirSlash, "dir-slash", "d", true, "Append a slash to directory names.")
|
flags.BoolVarP(&dirSlash, "dir-slash", "d", true, "Append a slash to directory names.")
|
||||||
|
flags.VarP(&hashType, "hash", "", "Use this hash when `h` is used in the format MD5|SHA-1|DropboxHash")
|
||||||
commandDefintion.Flags().BoolVarP(&recurse, "recursive", "R", false, "Recurse into the listing.")
|
commandDefintion.Flags().BoolVarP(&recurse, "recursive", "R", false, "Recurse into the listing.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,10 +46,18 @@ output:
|
|||||||
p - path
|
p - path
|
||||||
s - size
|
s - size
|
||||||
t - modification time
|
t - modification time
|
||||||
|
h - hash
|
||||||
|
|
||||||
So if you wanted the path, size and modification time, you would use
|
So if you wanted the path, size and modification time, you would use
|
||||||
--format "pst", or maybe --format "tsp" to put the path last.
|
--format "pst", or maybe --format "tsp" to put the path last.
|
||||||
|
|
||||||
|
If you specify "h" in the format you will get the MD5 hash by default,
|
||||||
|
use the "--hash" flag to change which hash you want. Note that this
|
||||||
|
can be returned as an empty string if it isn't available on the object
|
||||||
|
(and for directories), "ERROR" if there was an error reading it from
|
||||||
|
the object and "UNSUPPORTED" if that object does not support that hash
|
||||||
|
type.
|
||||||
|
|
||||||
By default the separator is ";" this can be changed with the
|
By default the separator is ";" this can be changed with the
|
||||||
--separator flag. Note that separators aren't escaped in the path so
|
--separator flag. Note that separators aren't escaped in the path so
|
||||||
putting it last is a good strategy.
|
putting it last is a good strategy.
|
||||||
@ -76,6 +86,8 @@ func Lsf(fsrc fs.Fs, out io.Writer) error {
|
|||||||
list.AddModTime()
|
list.AddModTime()
|
||||||
case 's':
|
case 's':
|
||||||
list.AddSize()
|
list.AddSize()
|
||||||
|
case 'h':
|
||||||
|
list.AddHash(hashType)
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("Unknown format character %q", char)
|
return errors.Errorf("Unknown format character %q", char)
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,16 @@ subdir
|
|||||||
321
|
321
|
||||||
1234
|
1234
|
||||||
-1
|
-1
|
||||||
|
`, buf.String())
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
format = "hp"
|
||||||
|
err = Lsf(f, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `d41d8cd98f00b204e9800998ecf8427e;file1
|
||||||
|
409d6c19451dd39d4a94e42d2ff2c834;file2
|
||||||
|
9b4c8a5e36d3be7e2c4b1d75ded8c8a1;file3
|
||||||
|
;subdir
|
||||||
`, buf.String())
|
`, buf.String())
|
||||||
|
|
||||||
buf = new(bytes.Buffer)
|
buf = new(bytes.Buffer)
|
||||||
|
26
fs/hash.go
26
fs/hash.go
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ncw/rclone/dropbox/dbhash"
|
"github.com/ncw/rclone/dropbox/dbhash"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashType indicates a standard hashing algorithm
|
// HashType indicates a standard hashing algorithm
|
||||||
@ -87,6 +88,31 @@ func (h HashType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set a HashType from a flag
|
||||||
|
func (h *HashType) Set(s string) error {
|
||||||
|
switch s {
|
||||||
|
case "None":
|
||||||
|
*h = HashNone
|
||||||
|
case "MD5":
|
||||||
|
*h = HashMD5
|
||||||
|
case "SHA-1":
|
||||||
|
*h = HashSHA1
|
||||||
|
case "DropboxHash":
|
||||||
|
*h = HashDropbox
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Unknown hash type %q", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the value
|
||||||
|
func (h HashType) Type() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check it satisfies the interface
|
||||||
|
var _ pflag.Value = (*HashType)(nil)
|
||||||
|
|
||||||
// hashFromTypes will return hashers for all the requested types.
|
// hashFromTypes will return hashers for all the requested types.
|
||||||
// The types must be a subset of SupportedHashes,
|
// The types must be a subset of SupportedHashes,
|
||||||
// and this function must support all types.
|
// and this function must support all types.
|
||||||
|
@ -1068,17 +1068,24 @@ func DropboxHashSum(f Fs, w io.Writer) error {
|
|||||||
return hashLister(HashDropbox, f, w)
|
return hashLister(HashDropbox, f, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashSum returns the human readable hash for ht passed in. This may
|
||||||
|
// be UNSUPPORTED or ERROR.
|
||||||
|
func hashSum(ht HashType, o Object) string {
|
||||||
|
Stats.Checking(o.Remote())
|
||||||
|
sum, err := o.Hash(ht)
|
||||||
|
Stats.DoneChecking(o.Remote())
|
||||||
|
if err == ErrHashUnsupported {
|
||||||
|
sum = "UNSUPPORTED"
|
||||||
|
} else if err != nil {
|
||||||
|
Debugf(o, "Failed to read %v: %v", ht, err)
|
||||||
|
sum = "ERROR"
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
func hashLister(ht HashType, f Fs, w io.Writer) error {
|
func hashLister(ht HashType, f Fs, w io.Writer) error {
|
||||||
return ListFn(f, func(o Object) {
|
return ListFn(f, func(o Object) {
|
||||||
Stats.Checking(o.Remote())
|
sum := hashSum(ht, o)
|
||||||
sum, err := o.Hash(ht)
|
|
||||||
Stats.DoneChecking(o.Remote())
|
|
||||||
if err == ErrHashUnsupported {
|
|
||||||
sum = "UNSUPPORTED"
|
|
||||||
} else if err != nil {
|
|
||||||
Debugf(o, "Failed to read %v: %v", ht, err)
|
|
||||||
sum = "ERROR"
|
|
||||||
}
|
|
||||||
syncFprintf(w, "%*s %s\n", HashWidth[ht], sum, o.Remote())
|
syncFprintf(w, "%*s %s\n", HashWidth[ht], sum, o.Remote())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1792,6 +1799,7 @@ type ListFormat struct {
|
|||||||
dirSlash bool
|
dirSlash bool
|
||||||
output []func() string
|
output []func() string
|
||||||
entry DirEntry
|
entry DirEntry
|
||||||
|
hash bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSeparator changes separator in struct
|
// SetSeparator changes separator in struct
|
||||||
@ -1816,7 +1824,9 @@ func (l *ListFormat) AddModTime() {
|
|||||||
|
|
||||||
// AddSize adds file's size to output
|
// AddSize adds file's size to output
|
||||||
func (l *ListFormat) AddSize() {
|
func (l *ListFormat) AddSize() {
|
||||||
l.AppendOutput(func() string { return strconv.FormatInt(l.entry.Size(), 10) })
|
l.AppendOutput(func() string {
|
||||||
|
return strconv.FormatInt(l.entry.Size(), 10)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPath adds path to file to output
|
// AddPath adds path to file to output
|
||||||
@ -1831,6 +1841,17 @@ func (l *ListFormat) AddPath() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddHash adds the hash of the type given to the output
|
||||||
|
func (l *ListFormat) AddHash(ht HashType) {
|
||||||
|
l.AppendOutput(func() string {
|
||||||
|
o, ok := l.entry.(Object)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return hashSum(ht, o)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// AppendOutput adds string generated by specific function to printed output
|
// AppendOutput adds string generated by specific function to printed output
|
||||||
func (l *ListFormat) AppendOutput(functionToAppend func() string) {
|
func (l *ListFormat) AppendOutput(functionToAppend func() string) {
|
||||||
if len(l.output) > 0 {
|
if len(l.output) > 0 {
|
||||||
|
@ -999,4 +999,20 @@ func TestListFormat(t *testing.T) {
|
|||||||
list.SetSeparator("__SEP__")
|
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__a__SEP__"+items[0].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[0], &list))
|
||||||
assert.Equal(t, fmt.Sprintf("%d", items[1].Size())+"__SEP__subdir/__SEP__"+items[1].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[1], &list))
|
assert.Equal(t, fmt.Sprintf("%d", items[1].Size())+"__SEP__subdir/__SEP__"+items[1].ModTime().Format("2006-01-02 15:04:05"), fs.ListFormatted(&items[1], &list))
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
ht fs.HashType
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{fs.HashMD5, "0cc175b9c0f1b6a831c399e269772661"},
|
||||||
|
{fs.HashSHA1, "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"},
|
||||||
|
{fs.HashDropbox, "bf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8"},
|
||||||
|
} {
|
||||||
|
list.SetOutput(nil)
|
||||||
|
list.AddHash(test.ht)
|
||||||
|
got := fs.ListFormatted(&items[0], &list)
|
||||||
|
if got != "UNSUPPORTED" {
|
||||||
|
assert.Equal(t, test.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user