346 lines · 10.0 KB · Go
RawDownload
package cmd import (	"fmt"	"log"	"os"	"regexp"	"strings" 	"github.com/rclone/rclone/fs"	"github.com/rclone/rclone/fs/config/configflags"	"github.com/rclone/rclone/fs/filter/filterflags"	"github.com/rclone/rclone/fs/log/logflags"	"github.com/rclone/rclone/fs/rc/rcflags"	"github.com/rclone/rclone/lib/atexit"	"github.com/spf13/cobra"	"github.com/spf13/pflag") // Root is the main rclone commandvar Root = &cobra.Command{	Use:   "rclone",	Short: "Show help for rclone commands, flags and backends.",	Long: `Rclone syncs files to and from cloud storage providers as well asmounting them, listing them in lots of different ways. See the home page (https://rclone.org/) for installation, usage,documentation, changelog and configuration walkthroughs. `,	PersistentPostRun: func(cmd *cobra.Command, args []string) {		fs.Debugf("rclone", "Version %q finishing with parameters %q", fs.Version, os.Args)		atexit.Run()	},	BashCompletionFunction: bashCompletionFunc,	DisableAutoGenTag:      true,} const (	bashCompletionFunc = `__rclone_custom_func() {    if [[ ${#COMPREPLY[@]} -eq 0 ]]; then        local cur cword prev words        if declare -F _init_completion > /dev/null; then            _init_completion -n : || return        else            __rclone_init_completion -n : || return        fi	local rclone=(command rclone --ask-password=false)        if [[ $cur != *:* ]]; then            local ifs=$IFS            IFS=$'\n'            local remotes=($("${rclone[@]}" listremotes 2> /dev/null))            IFS=$ifs            local remote            for remote in "${remotes[@]}"; do                [[ $remote != $cur* ]] || COMPREPLY+=("$remote")            done            if [[ ${COMPREPLY[@]} ]]; then                local paths=("$cur"*)                [[ ! -f ${paths[0]} ]] || COMPREPLY+=("${paths[@]}")            fi        else            local path=${cur#*:}            if [[ $path == */* ]]; then                local prefix=$(eval printf '%s' "${path%/*}")            else                local prefix=            fi            local ifs=$IFS            IFS=$'\n'            local lines=($("${rclone[@]}" lsf "${cur%%:*}:$prefix" 2> /dev/null))            IFS=$ifs            local line            for line in "${lines[@]}"; do                local reply=${prefix:+$prefix/}$line                [[ $reply != $path* ]] || COMPREPLY+=("$reply")            done	    [[ ! ${COMPREPLY[@]} || $(type -t compopt) != builtin ]] || compopt -o filenames        fi        [[ ! ${COMPREPLY[@]} || $(type -t compopt) != builtin ]] || compopt -o nospace    fi}`) // GeneratingDocs is set by rclone gendocs to alter the format of the// output suitable for the documentation.var GeneratingDocs = false // root help commandvar helpCommand = &cobra.Command{	Use:   "help",	Short: Root.Short,	Long:  Root.Long,	Run: func(command *cobra.Command, args []string) {		Root.SetOutput(os.Stdout)		_ = Root.Usage()	},} // to filter the flags withvar flagsRe *regexp.Regexp // Show the flagsvar helpFlags = &cobra.Command{	Use:   "flags [<regexp to match>]",	Short: "Show the global flags for rclone",	Run: func(command *cobra.Command, args []string) {		if len(args) > 0 {			re, err := regexp.Compile(args[0])			if err != nil {				log.Fatalf("Failed to compile flags regexp: %v", err)			}			flagsRe = re		}		if GeneratingDocs {			Root.SetUsageTemplate(docFlagsTemplate)		} else {			Root.SetOutput(os.Stdout)		}		_ = command.Usage()	},} // Show the backendsvar helpBackends = &cobra.Command{	Use:   "backends",	Short: "List the backends available",	Run: func(command *cobra.Command, args []string) {		showBackends()	},} // Show a single backendvar helpBackend = &cobra.Command{	Use:   "backend <name>",	Short: "List full info about a backend",	Run: func(command *cobra.Command, args []string) {		if len(args) == 0 {			Root.SetOutput(os.Stdout)			_ = command.Usage()			return		}		showBackend(args[0])	},} // runRoot implements the main rclone command with no subcommandsfunc runRoot(cmd *cobra.Command, args []string) {	if version {		ShowVersion()		resolveExitCode(nil)	} else {		_ = cmd.Usage()		if len(args) > 0 {			_, _ = fmt.Fprintf(os.Stderr, "Command not found.\n")		}		resolveExitCode(errorCommandNotFound)	}} // setupRootCommand sets default usage, help, and error handling for// the root command.//// Helpful example: http://rtfcode.com/xref/moby-17.03.2-ce/cli/cobra.gofunc setupRootCommand(rootCmd *cobra.Command) {	// Add global flags	configflags.AddFlags(pflag.CommandLine)	filterflags.AddFlags(pflag.CommandLine)	rcflags.AddFlags(pflag.CommandLine)	logflags.AddFlags(pflag.CommandLine) 	Root.Run = runRoot	Root.Flags().BoolVarP(&version, "version", "V", false, "Print the version number") 	cobra.AddTemplateFunc("showGlobalFlags", func(cmd *cobra.Command) bool {		return cmd.CalledAs() == "flags"	})	cobra.AddTemplateFunc("showCommands", func(cmd *cobra.Command) bool {		return cmd.CalledAs() != "flags"	})	cobra.AddTemplateFunc("showLocalFlags", func(cmd *cobra.Command) bool {		// Don't show local flags (which are the global ones on the root) on "rclone" and		// "rclone help" (which shows the global help)		return cmd.CalledAs() != "rclone" && cmd.CalledAs() != ""	})	cobra.AddTemplateFunc("backendFlags", func(cmd *cobra.Command, include bool) *pflag.FlagSet {		backendFlagSet := pflag.NewFlagSet("Backend Flags", pflag.ExitOnError)		cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {			matched := flagsRe == nil || flagsRe.MatchString(flag.Name)			if _, ok := backendFlags[flag.Name]; matched && ok == include {				backendFlagSet.AddFlag(flag)			}		})		return backendFlagSet	})	rootCmd.SetUsageTemplate(usageTemplate)	// rootCmd.SetHelpTemplate(helpTemplate)	// rootCmd.SetFlagErrorFunc(FlagErrorFunc)	rootCmd.SetHelpCommand(helpCommand)	// rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")	// rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") 	rootCmd.AddCommand(helpCommand)	helpCommand.AddCommand(helpFlags)	helpCommand.AddCommand(helpBackends)	helpCommand.AddCommand(helpBackend) 	cobra.OnInitialize(initConfig) } var usageTemplate = `Usage:{{if .Runnable}}  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}  {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases:  {{.NameAndAliases}}{{end}}{{if .HasExample}} Examples:{{.Example}}{{end}}{{if and (showCommands .) .HasAvailableSubCommands}} Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if and (showLocalFlags .) .HasAvailableLocalFlags}} Flags:{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if and (showGlobalFlags .) .HasAvailableInheritedFlags}} Global Flags:{{(backendFlags . false).FlagUsages | trimTrailingWhitespaces}} Backend Flags:{{(backendFlags . true).FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}} Use "rclone [command] --help" for more information about a command.Use "rclone help flags" for to see the global flags.Use "rclone help backends" for a list of supported services.` var docFlagsTemplate = `---title: "Global Flags"description: "Rclone Global Flags"--- # Global Flags This describes the global flags available to every rclone commandsplit into two groups, non backend and backend flags. ## Non Backend Flags These flags are available for every command. ` + "```" + `{{(backendFlags . false).FlagUsages | trimTrailingWhitespaces}}` + "```" + ` ## Backend Flags These flags are available for every command. They control the backendsand may be set in the config file. ` + "```" + `{{(backendFlags . true).FlagUsages | trimTrailingWhitespaces}}` + "```" + `` // show all the backendsfunc showBackends() {	fmt.Printf("All rclone backends:\n\n")	for _, backend := range fs.Registry {		fmt.Printf("  %-12s %s\n", backend.Prefix, backend.Description)	}	fmt.Printf("\nTo see more info about a particular backend use:\n")	fmt.Printf("  rclone help backend <name>\n")} func quoteString(v interface{}) string {	switch v.(type) {	case string:		return fmt.Sprintf("%q", v)	}	return fmt.Sprint(v)} // show a single backendfunc showBackend(name string) {	backend, err := fs.Find(name)	if err != nil {		log.Fatal(err)	}	var standardOptions, advancedOptions fs.Options	done := map[string]struct{}{}	for _, opt := range backend.Options {		// Skip if done already (eg with Provider options)		if _, doneAlready := done[opt.Name]; doneAlready {			continue		}		if opt.Advanced {			advancedOptions = append(advancedOptions, opt)		} else {			standardOptions = append(standardOptions, opt)		}	}	optionsType := "standard"	for _, opts := range []fs.Options{standardOptions, advancedOptions} {		if len(opts) == 0 {			optionsType = "advanced"			continue		}		fmt.Printf("### %s Options\n\n", strings.Title(optionsType))		fmt.Printf("Here are the %s options specific to %s (%s).\n\n", optionsType, backend.Name, backend.Description)		optionsType = "advanced"		for _, opt := range opts {			done[opt.Name] = struct{}{}			shortOpt := ""			if opt.ShortOpt != "" {				shortOpt = fmt.Sprintf(" / -%s", opt.ShortOpt)			}			fmt.Printf("#### --%s%s\n\n", opt.FlagName(backend.Prefix), shortOpt)			fmt.Printf("%s\n\n", opt.Help)			if opt.IsPassword {				fmt.Printf("**NB** Input to this must be obscured - see [rclone obscure](/commands/rclone_obscure/).\n\n")			}			fmt.Printf("- Config:      %s\n", opt.Name)			fmt.Printf("- Env Var:     %s\n", opt.EnvVarName(backend.Prefix))			fmt.Printf("- Type:        %s\n", opt.Type())			fmt.Printf("- Default:     %s\n", quoteString(opt.GetValue()))			if len(opt.Examples) > 0 {				fmt.Printf("- Examples:\n")				for _, ex := range opt.Examples {					fmt.Printf("    - %s\n", quoteString(ex.Value))					for _, line := range strings.Split(ex.Help, "\n") {						fmt.Printf("        - %s\n", line)					}				}			}			fmt.Printf("\n")		}	}}