From 8656bd2bb05f4017ae6e5368741366cbbc878fc9 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 7 Aug 2018 22:51:06 +0100 Subject: [PATCH] fs: Allow on the fly remotes with :backend: syntax - fixes #2449 This change allows remotes to be created on the fly without a config file by using the remote type prefixed with a : as the remote name, Eg :s3: to make an s3 remote. This assumes the user is supplying the backend config via command line flags or environment variables. --- docs/content/docs.md | 42 ++++++++++++++++++++++++++++++++++++++++++ docs/content/http.md | 16 +++------------- fs/fs.go | 12 ++++++++---- fs/fspath/path.go | 2 +- fs/fspath/path_test.go | 10 ++++++++++ 5 files changed, 64 insertions(+), 18 deletions(-) diff --git a/docs/content/docs.md b/docs/content/docs.md index 390d6ae45..c7e9df186 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -133,6 +133,48 @@ It is recommended to use `copy` when copying individual files, not `sync`. They have pretty much the same effect but `copy` will use a lot less memory. +Syntax of remote paths +---------------------- + +The syntax of the paths passed to the rclone command are as follows. + +### /path/to/dir + +This refers to the local file system. + +On Windows only `\` may be used instead of `/` in local paths +**only**, non local paths must use `/`. + +These paths needn't start with a leading `/` - if they don't then they +will be relative to the current directory. + +### remote:path/to/dir + +This refers to a directory `path/to/dir` on `remote:` as defined in +the config file (configured with `rclone config`). + +### remote:/path/to/dir + +On most backends this is refers to the same directory as +`remote:path/to/dir` and that format should be preferred. On a very +small number of remotes (FTP, SFTP, Dropbox for business) this will +refer to a different directory. On these, paths without a leading `/` +will refer to your "home" directory and paths with a leading `/` will +refer to the root. + +### :backend:path/to/dir + +This is an advanced form for creating remotes on the fly. `backend` +should be the name or prefix of a backend (the `type` in the config +file) and all the configuration for the backend should be provided on +the command line (or in environment variables). + +Eg + + rclone lsd --http-url https://pub.rclone.org :http: + +Which lists all the directories in `pub.rclone.org`. + Quoting and the shell --------------------- diff --git a/docs/content/http.md b/docs/content/http.md index 27944e907..83bff5871 100644 --- a/docs/content/http.md +++ b/docs/content/http.md @@ -121,17 +121,7 @@ No checksums are stored. ### Usage without a config file ### -Note that since only two environment variable need to be set, it is -easy to use without a config file like this. +Since the http remote only has one config parameter it is easy to use +without a config file: -``` -RCLONE_CONFIG_ZZ_TYPE=http RCLONE_CONFIG_ZZ_URL=https://beta.rclone.org rclone lsd zz: -``` - -Or if you prefer - -``` -export RCLONE_CONFIG_ZZ_TYPE=http -export RCLONE_CONFIG_ZZ_URL=https://beta.rclone.org -rclone lsd zz: -``` + rclone lsd --http-url https://beta.rclone.org :http: diff --git a/fs/fs.go b/fs/fs.go index 445e050c1..18e2ece0d 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -859,10 +859,14 @@ func ParseRemote(path string) (fsInfo *RegInfo, configName, fsPath string, err e var fsName string var ok bool if configName != "" { - m := ConfigMap(nil, configName) - fsName, ok = m.Get("type") - if !ok { - return nil, "", "", ErrorNotFoundInConfigFile + if strings.HasPrefix(configName, ":") { + fsName = configName[1:] + } else { + m := ConfigMap(nil, configName) + fsName, ok = m.Get("type") + if !ok { + return nil, "", "", ErrorNotFoundInConfigFile + } } } else { fsName = "local" diff --git a/fs/fspath/path.go b/fs/fspath/path.go index 65e9ec751..baa89bbd2 100644 --- a/fs/fspath/path.go +++ b/fs/fspath/path.go @@ -10,7 +10,7 @@ import ( ) // Matcher is a pattern to match an rclone URL -var Matcher = regexp.MustCompile(`^([\w_ -]+):(.*)$`) +var Matcher = regexp.MustCompile(`^(:?[\w_ -]+):(.*)$`) // Parse deconstructs a remote path into configName and fsPath // diff --git a/fs/fspath/path_test.go b/fs/fspath/path_test.go index 1a50ed3f3..d5b41a473 100644 --- a/fs/fspath/path_test.go +++ b/fs/fspath/path_test.go @@ -16,6 +16,7 @@ func TestParse(t *testing.T) { {"path/to/file", "", "path/to/file"}, {"remote:path/to/file", "remote", "path/to/file"}, {"remote:/path/to/file", "remote", "/path/to/file"}, + {":backend:/path/to/file", ":backend", "/path/to/file"}, } { gotConfigName, gotFsPath := Parse(test.in) assert.Equal(t, test.wantConfigName, gotConfigName) @@ -28,12 +29,21 @@ func TestSplit(t *testing.T) { remote, wantParent, wantLeaf string }{ {"", "", ""}, + {"remote:", "remote:", ""}, {"remote:potato", "remote:", "potato"}, {"remote:/", "remote:/", ""}, {"remote:/potato", "remote:/", "potato"}, {"remote:/potato/potato", "remote:/potato/", "potato"}, {"remote:potato/sausage", "remote:potato/", "sausage"}, + + {":remote:", ":remote:", ""}, + {":remote:potato", ":remote:", "potato"}, + {":remote:/", ":remote:/", ""}, + {":remote:/potato", ":remote:/", "potato"}, + {":remote:/potato/potato", ":remote:/potato/", "potato"}, + {":remote:potato/sausage", ":remote:potato/", "sausage"}, + {"/", "/", ""}, {"/root", "/", "root"}, {"/a/b", "/a/", "b"},