diff --git a/docs/content/docs.md b/docs/content/docs.md index 041aeb4ea..768b331d8 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -107,7 +107,28 @@ objects in it, use purge for that. ### rclone purge remote:path ### -Remove the path and all of its contents. +Remove the path and all of its contents. Note that this does not obey +include/exclude filters - everything will be removed. Use `delete` if +you want to selectively delete files. + +### rclone delete remote:path ### + +Remove the contents of path. Unlike `purge` it obeys include/exclude +filters so can be used to selectively delete files. + +Eg delete all files bigger than 100MBytes + +Check what would be deleted first (use either) + + rclone --min-size 100M lsl remote:path + rclone --dry-run --min-size 100M delete remote:path + +Then delete + + rclone --min-size 100M delete remote:path + +That reads "delete everything with a minimum size of 100 MB", hence +delete all files bigger than 100MBytes. ### rclone check source:path dest:path ### diff --git a/docs/content/filtering.md b/docs/content/filtering.md index 3f3ee6cd1..84286ab5b 100644 --- a/docs/content/filtering.md +++ b/docs/content/filtering.md @@ -10,7 +10,7 @@ Rclone has a sophisticated set of include and exclude rules. Some of these are based on patterns and some on other things like file size. The filters are applied for the `copy`, `sync`, `move`, `ls`, `lsl`, -`md5sum`, `sha1sum`, `size` and `check` operations. +`md5sum`, `sha1sum`, `size`, `delete` and `check` operations. Note that `purge` does not obey the filters. Each path as it passes through rclone is matched against the include diff --git a/fs/operations.go b/fs/operations.go index c072a103d..a6a0a87d3 100644 --- a/fs/operations.go +++ b/fs/operations.go @@ -891,3 +891,20 @@ func Purge(f Fs) error { } return nil } + +// Delete removes all the contents of a container. Unlike Purge, it +// obeys includes and excludes. +func Delete(f Fs) error { + wg := new(sync.WaitGroup) + delete := make(ObjectsChan, Config.Transfers) + wg.Add(1) + go func() { + defer wg.Done() + DeleteFiles(delete) + }() + err := ListFn(f, func(o Object) { + delete <- o + }) + close(delete) + return err +} diff --git a/fs/operations_test.go b/fs/operations_test.go index 6930f1515..4c0654114 100644 --- a/fs/operations_test.go +++ b/fs/operations_test.go @@ -868,6 +868,26 @@ func TestCount(t *testing.T) { } } +func TestDelete(t *testing.T) { + r := NewRun(t) + defer r.Finalise() + file1 := r.WriteObject("small", "1234567890", t2) // 10 bytes + file2 := r.WriteObject("medium", "------------------------------------------------------------", t1) // 60 bytes + file3 := r.WriteObject("large", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", t1) // 100 bytes + fstest.CheckItems(t, r.fremote, file1, file2, file3) + + fs.Config.Filter.MaxSize = 60 + defer func() { + fs.Config.Filter.MaxSize = 0 + }() + + err := fs.Delete(r.fremote) + if err != nil { + t.Fatalf("Sync failed: %v", err) + } + fstest.CheckItems(t, r.fremote, file3) +} + func TestCheck(t *testing.T) { r := NewRun(t) defer r.Finalise() diff --git a/rclone.go b/rclone.go index 16a8d4631..ae8cee399 100644 --- a/rclone.go +++ b/rclone.go @@ -206,7 +206,8 @@ var Commands = []Command{ Name: "purge", ArgsHelp: "remote:path", Help: ` - Remove the path and all of its contents.`, + Remove the path and all of its contents. Does not obey + filters - use remove for that.`, Run: func(fdst, fsrc fs.Fs) error { return fs.Purge(fdst) }, @@ -214,6 +215,18 @@ var Commands = []Command{ MaxArgs: 1, Retry: true, }, + { + Name: "delete", + ArgsHelp: "remote:path", + Help: ` + Remove the contents of path. Obeys include/exclude filters.`, + Run: func(fdst, fsrc fs.Fs) error { + return fs.Delete(fdst) + }, + MinArgs: 1, + MaxArgs: 1, + Retry: true, + }, { Name: "check", ArgsHelp: "source:path dest:path",