github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/help.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "regexp" 8 "strings" 9 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/config/configflags" 12 "github.com/rclone/rclone/fs/filter/filterflags" 13 "github.com/rclone/rclone/fs/log/logflags" 14 "github.com/rclone/rclone/fs/rc/rcflags" 15 "github.com/rclone/rclone/lib/atexit" 16 "github.com/spf13/cobra" 17 "github.com/spf13/pflag" 18 ) 19 20 // Root is the main rclone command 21 var Root = &cobra.Command{ 22 Use: "rclone", 23 Short: "Show help for rclone commands, flags and backends.", 24 Long: ` 25 Rclone syncs files to and from cloud storage providers as well as 26 mounting them, listing them in lots of different ways. 27 28 See the home page (https://rclone.org/) for installation, usage, 29 documentation, changelog and configuration walkthroughs. 30 31 `, 32 PersistentPostRun: func(cmd *cobra.Command, args []string) { 33 fs.Debugf("rclone", "Version %q finishing with parameters %q", fs.Version, os.Args) 34 atexit.Run() 35 }, 36 BashCompletionFunction: bashCompletionFunc, 37 DisableAutoGenTag: true, 38 } 39 40 const ( 41 bashCompletionFunc = ` 42 __rclone_custom_func() { 43 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then 44 local cur cword prev words 45 if declare -F _init_completion > /dev/null; then 46 _init_completion -n : || return 47 else 48 __rclone_init_completion -n : || return 49 fi 50 local rclone=(command rclone --ask-password=false) 51 if [[ $cur != *:* ]]; then 52 local ifs=$IFS 53 IFS=$'\n' 54 local remotes=($("${rclone[@]}" listremotes 2> /dev/null)) 55 IFS=$ifs 56 local remote 57 for remote in "${remotes[@]}"; do 58 [[ $remote != $cur* ]] || COMPREPLY+=("$remote") 59 done 60 if [[ ${COMPREPLY[@]} ]]; then 61 local paths=("$cur"*) 62 [[ ! -f ${paths[0]} ]] || COMPREPLY+=("${paths[@]}") 63 fi 64 else 65 local path=${cur#*:} 66 if [[ $path == */* ]]; then 67 local prefix=$(eval printf '%s' "${path%/*}") 68 else 69 local prefix= 70 fi 71 local ifs=$IFS 72 IFS=$'\n' 73 local lines=($("${rclone[@]}" lsf "${cur%%:*}:$prefix" 2> /dev/null)) 74 IFS=$ifs 75 local line 76 for line in "${lines[@]}"; do 77 local reply=${prefix:+$prefix/}$line 78 [[ $reply != $path* ]] || COMPREPLY+=("$reply") 79 done 80 [[ ! ${COMPREPLY[@]} || $(type -t compopt) != builtin ]] || compopt -o filenames 81 fi 82 [[ ! ${COMPREPLY[@]} || $(type -t compopt) != builtin ]] || compopt -o nospace 83 fi 84 } 85 ` 86 ) 87 88 // GeneratingDocs is set by rclone gendocs to alter the format of the 89 // output suitable for the documentation. 90 var GeneratingDocs = false 91 92 // root help command 93 var helpCommand = &cobra.Command{ 94 Use: "help", 95 Short: Root.Short, 96 Long: Root.Long, 97 Run: func(command *cobra.Command, args []string) { 98 Root.SetOutput(os.Stdout) 99 _ = Root.Usage() 100 }, 101 } 102 103 // to filter the flags with 104 var flagsRe *regexp.Regexp 105 106 // Show the flags 107 var helpFlags = &cobra.Command{ 108 Use: "flags [<regexp to match>]", 109 Short: "Show the global flags for rclone", 110 Run: func(command *cobra.Command, args []string) { 111 if len(args) > 0 { 112 re, err := regexp.Compile(args[0]) 113 if err != nil { 114 log.Fatalf("Failed to compile flags regexp: %v", err) 115 } 116 flagsRe = re 117 } 118 if GeneratingDocs { 119 Root.SetUsageTemplate(docFlagsTemplate) 120 } else { 121 Root.SetOutput(os.Stdout) 122 } 123 _ = command.Usage() 124 }, 125 } 126 127 // Show the backends 128 var helpBackends = &cobra.Command{ 129 Use: "backends", 130 Short: "List the backends available", 131 Run: func(command *cobra.Command, args []string) { 132 showBackends() 133 }, 134 } 135 136 // Show a single backend 137 var helpBackend = &cobra.Command{ 138 Use: "backend <name>", 139 Short: "List full info about a backend", 140 Run: func(command *cobra.Command, args []string) { 141 if len(args) == 0 { 142 Root.SetOutput(os.Stdout) 143 _ = command.Usage() 144 return 145 } 146 showBackend(args[0]) 147 }, 148 } 149 150 // runRoot implements the main rclone command with no subcommands 151 func runRoot(cmd *cobra.Command, args []string) { 152 if version { 153 ShowVersion() 154 resolveExitCode(nil) 155 } else { 156 _ = cmd.Usage() 157 if len(args) > 0 { 158 _, _ = fmt.Fprintf(os.Stderr, "Command not found.\n") 159 } 160 resolveExitCode(errorCommandNotFound) 161 } 162 } 163 164 // setupRootCommand sets default usage, help, and error handling for 165 // the root command. 166 // 167 // Helpful example: http://rtfcode.com/xref/moby-17.03.2-ce/cli/cobra.go 168 func setupRootCommand(rootCmd *cobra.Command) { 169 // Add global flags 170 configflags.AddFlags(pflag.CommandLine) 171 filterflags.AddFlags(pflag.CommandLine) 172 rcflags.AddFlags(pflag.CommandLine) 173 logflags.AddFlags(pflag.CommandLine) 174 175 Root.Run = runRoot 176 Root.Flags().BoolVarP(&version, "version", "V", false, "Print the version number") 177 178 cobra.AddTemplateFunc("showGlobalFlags", func(cmd *cobra.Command) bool { 179 return cmd.CalledAs() == "flags" 180 }) 181 cobra.AddTemplateFunc("showCommands", func(cmd *cobra.Command) bool { 182 return cmd.CalledAs() != "flags" 183 }) 184 cobra.AddTemplateFunc("showLocalFlags", func(cmd *cobra.Command) bool { 185 // Don't show local flags (which are the global ones on the root) on "rclone" and 186 // "rclone help" (which shows the global help) 187 return cmd.CalledAs() != "rclone" && cmd.CalledAs() != "" 188 }) 189 cobra.AddTemplateFunc("backendFlags", func(cmd *cobra.Command, include bool) *pflag.FlagSet { 190 backendFlagSet := pflag.NewFlagSet("Backend Flags", pflag.ExitOnError) 191 cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { 192 matched := flagsRe == nil || flagsRe.MatchString(flag.Name) 193 if _, ok := backendFlags[flag.Name]; matched && ok == include { 194 backendFlagSet.AddFlag(flag) 195 } 196 }) 197 return backendFlagSet 198 }) 199 rootCmd.SetUsageTemplate(usageTemplate) 200 // rootCmd.SetHelpTemplate(helpTemplate) 201 // rootCmd.SetFlagErrorFunc(FlagErrorFunc) 202 rootCmd.SetHelpCommand(helpCommand) 203 // rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") 204 // rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") 205 206 rootCmd.AddCommand(helpCommand) 207 helpCommand.AddCommand(helpFlags) 208 helpCommand.AddCommand(helpBackends) 209 helpCommand.AddCommand(helpBackend) 210 211 cobra.OnInitialize(initConfig) 212 213 } 214 215 var usageTemplate = `Usage:{{if .Runnable}} 216 {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} 217 {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} 218 219 Aliases: 220 {{.NameAndAliases}}{{end}}{{if .HasExample}} 221 222 Examples: 223 {{.Example}}{{end}}{{if and (showCommands .) .HasAvailableSubCommands}} 224 225 Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} 226 {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if and (showLocalFlags .) .HasAvailableLocalFlags}} 227 228 Flags: 229 {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if and (showGlobalFlags .) .HasAvailableInheritedFlags}} 230 231 Global Flags: 232 {{(backendFlags . false).FlagUsages | trimTrailingWhitespaces}} 233 234 Backend Flags: 235 {{(backendFlags . true).FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} 236 237 Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} 238 {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}} 239 240 Use "rclone [command] --help" for more information about a command. 241 Use "rclone help flags" for to see the global flags. 242 Use "rclone help backends" for a list of supported services. 243 ` 244 245 var docFlagsTemplate = `--- 246 title: "Global Flags" 247 description: "Rclone Global Flags" 248 --- 249 250 # Global Flags 251 252 This describes the global flags available to every rclone command 253 split into two groups, non backend and backend flags. 254 255 ## Non Backend Flags 256 257 These flags are available for every command. 258 259 ` + "```" + ` 260 {{(backendFlags . false).FlagUsages | trimTrailingWhitespaces}} 261 ` + "```" + ` 262 263 ## Backend Flags 264 265 These flags are available for every command. They control the backends 266 and may be set in the config file. 267 268 ` + "```" + ` 269 {{(backendFlags . true).FlagUsages | trimTrailingWhitespaces}} 270 ` + "```" + ` 271 ` 272 273 // show all the backends 274 func showBackends() { 275 fmt.Printf("All rclone backends:\n\n") 276 for _, backend := range fs.Registry { 277 fmt.Printf(" %-12s %s\n", backend.Prefix, backend.Description) 278 } 279 fmt.Printf("\nTo see more info about a particular backend use:\n") 280 fmt.Printf(" rclone help backend <name>\n") 281 } 282 283 func quoteString(v interface{}) string { 284 switch v.(type) { 285 case string: 286 return fmt.Sprintf("%q", v) 287 } 288 return fmt.Sprint(v) 289 } 290 291 // show a single backend 292 func showBackend(name string) { 293 backend, err := fs.Find(name) 294 if err != nil { 295 log.Fatal(err) 296 } 297 var standardOptions, advancedOptions fs.Options 298 done := map[string]struct{}{} 299 for _, opt := range backend.Options { 300 // Skip if done already (eg with Provider options) 301 if _, doneAlready := done[opt.Name]; doneAlready { 302 continue 303 } 304 if opt.Advanced { 305 advancedOptions = append(advancedOptions, opt) 306 } else { 307 standardOptions = append(standardOptions, opt) 308 } 309 } 310 optionsType := "standard" 311 for _, opts := range []fs.Options{standardOptions, advancedOptions} { 312 if len(opts) == 0 { 313 optionsType = "advanced" 314 continue 315 } 316 fmt.Printf("### %s Options\n\n", strings.Title(optionsType)) 317 fmt.Printf("Here are the %s options specific to %s (%s).\n\n", optionsType, backend.Name, backend.Description) 318 optionsType = "advanced" 319 for _, opt := range opts { 320 done[opt.Name] = struct{}{} 321 shortOpt := "" 322 if opt.ShortOpt != "" { 323 shortOpt = fmt.Sprintf(" / -%s", opt.ShortOpt) 324 } 325 fmt.Printf("#### --%s%s\n\n", opt.FlagName(backend.Prefix), shortOpt) 326 fmt.Printf("%s\n\n", opt.Help) 327 if opt.IsPassword { 328 fmt.Printf("**NB** Input to this must be obscured - see [rclone obscure](/commands/rclone_obscure/).\n\n") 329 } 330 fmt.Printf("- Config: %s\n", opt.Name) 331 fmt.Printf("- Env Var: %s\n", opt.EnvVarName(backend.Prefix)) 332 fmt.Printf("- Type: %s\n", opt.Type()) 333 fmt.Printf("- Default: %s\n", quoteString(opt.GetValue())) 334 if len(opt.Examples) > 0 { 335 fmt.Printf("- Examples:\n") 336 for _, ex := range opt.Examples { 337 fmt.Printf(" - %s\n", quoteString(ex.Value)) 338 for _, line := range strings.Split(ex.Help, "\n") { 339 fmt.Printf(" - %s\n", line) 340 } 341 } 342 } 343 fmt.Printf("\n") 344 } 345 } 346 }