From 4e671f23c8a3870eb1d10ec4e9331cc28178cb1e Mon Sep 17 00:00:00 2001 From: ren_jw Date: Sat, 3 Oct 2020 15:15:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=91=BD=E4=BB=A4=E8=A1=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=E5=8A=9F=E8=83=BD=20=E4=BF=AE=E6=94=B9add=20?= =?UTF-8?q?user=E5=AD=90=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 ++++++++++++-------- cmd/add.go | 28 +++++++++++++---------- cmd/complete.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ cmd/install.go | 28 ++++++++++++----------- cmd/root.go | 42 ++++++++++++++++++++++++++++++++++ sys/info.go | 2 +- util/execute.go | 34 ++++++++++++++++++++++++++- util/slice.go | 10 ++++++++ 8 files changed, 194 insertions(+), 36 deletions(-) create mode 100644 cmd/complete.go create mode 100644 util/slice.go diff --git a/README.md b/README.md index b3a5e5ad..b8328257 100644 --- a/README.md +++ b/README.md @@ -31,17 +31,24 @@ ## 安装 -1.[下载release版本](https://github.com/weiliang-ms/easyctl/releases/) +> 下载上传 -2.上传至/usr/sbin/下 +[下载release版本](https://github.com/weiliang-ms/easyctl/releases/) -3.添加执行权限 +上传至/usr/bin/下 - chmod +x /usr/sbin/easyctl +> 添加执行权限 + + chmod +x /usr/bin/easyctl -4.查看版本信息 +> 查看版本信息 easyctl version + +> 配置命令补全 + + easyctl completion bash > /etc/bash_completion.d/easyctl + source <(easyctl completion bash) # 命令介绍 @@ -65,11 +72,11 @@ 1.添加可登录的linux用户(password可省,默认密码:user123) - easyctl add username password + easyctl add userad -u username -p password 2.添加非登录linux用户 - easyctl add username --no-login=true + easyctl add -u username --no-login # close指令集 @@ -117,7 +124,7 @@ easyctl install docker [flags] - flags 可选 --offline=true --file=./v19.03.13.tar.gz (离线安装) + flags 可选 --offline --file=./v19.03.13.tar.gz (离线安装) > 在线安装样例 @@ -276,7 +283,7 @@ flag 8.主机host解析 -9.添加命令自动补全 +9.添加命令自动补全(已完成) ## 开源项目 diff --git a/cmd/add.go b/cmd/add.go index 865edbc0..69ae16fe 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -5,12 +5,18 @@ import ( "github.com/spf13/cobra" ) -var Nologin bool +var ( + Nologin bool + username string + password string +) func init() { addUserCmd.Flags().BoolVarP(&Nologin, "no-login", "n", false, "User type: no login") - addUserCmd.Flags().Parsed() + addUserCmd.Flags().StringVarP(&username, "username", "u", "", "user name") + addUserCmd.Flags().StringVarP(&password, "password", "p", "", "user password") + addUserCmd.MarkFlagRequired("username") addCmd.AddCommand(addUserCmd) rootCmd.AddCommand(addCmd) @@ -30,21 +36,19 @@ var addCmd = &cobra.Command{ // addUser命令 var addUserCmd = &cobra.Command{ - Use: "user [username] [password] [flags]", + Use: "user [flags]", Short: "add linux user through easyctl, password default value: user123", - Example: "\neasyctl add user user1 password" + - "\neasyctl add user user1 password --no-login", + Example: "\neasyctl add user -u user1 -p password" + + "\neasyctl add user -u user1 --no-login", Run: func(cmd *cobra.Command, args []string) { - addUser(args) + addUser() }, - Args: cobra.MinimumNArgs(1), + Args: cobra.NoArgs, } -func addUser(args []string) { - password := "" - username := args[0] - if len(args) > 1 { - password = args[1] +func addUser() { + if password == "" { + password = "user123" } sys.AddUser(username, password, !Nologin) } diff --git a/cmd/complete.go b/cmd/complete.go new file mode 100644 index 00000000..f6f77b6a --- /dev/null +++ b/cmd/complete.go @@ -0,0 +1,61 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "os" +) + +func init() { + rootCmd.AddCommand(completionCmd) +} + +var completionCmd = &cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate completion script", + Long: `To load completions: + +Bash: + +$ source <(yourprogram completion bash) + +# To load completions for each session, execute once: +Linux: + $ yourprogram completion bash > /etc/bash_completion.d/yourprogram +MacOS: + $ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram + +Zsh: + +# If shell completion is not already enabled in your environment you will need +# to enable it. You can execute the following once: + +$ echo "autoload -U compinit; compinit" >> ~/.zshrc + +# To load completions for each session, execute once: +$ yourprogram completion zsh > "${fpath[1]}/_yourprogram" + +# You will need to start a new shell for this setup to take effect. + +Fish: + +$ yourprogram completion fish | source + +# To load completions for each session, execute once: +$ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish +`, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cobra.ExactValidArgs(1), + Run: func(cmd *cobra.Command, args []string) { + switch args[0] { + case "bash": + cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + cmd.Root().GenPowerShellCompletion(os.Stdout) + } + }, +} diff --git a/cmd/install.go b/cmd/install.go index 83e49300..7d6e1f09 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -29,14 +29,14 @@ var ( func init() { netConnectErr = errors.New("网络连接异常,请选择离线方式安装...") installErr = errors.New("程序安装失败...") - installRedisCmd.Flags().BoolVarP(&offline, "offline", "o", false, "offline mode") - installRedisCmd.Flags().StringVarP(&redisPort, "port", "p", "6379", "Redis listen port") - installRedisCmd.Flags().StringVarP(&redisBindIP, "bind", "b", "0.0.0.0", "Redis bind address") - installRedisCmd.Flags().StringVarP(&redisPassword, "password", "a", "redis", "Redis password") - installRedisCmd.Flags().StringVarP(&redisDataDir, "data", "d", "/var/lib/redis", "Redis persistent directory") - installRedisCmd.Flags().StringVarP(&redisLogDir, "log-file", "", "/var/log/redis", "Redis logfile directory") - installRedisCmd.Flags().StringVarP(&filePath, "file", "f", "", "docker-x-x-x.tgz path") - installRedisCmd.Flags().StringVarP(&redisBinaryPath, "binary-path", "", "/usr/bin/", "redis-* binary file path") + installRedisCmd.Flags().BoolVarP(&offline, "offline", "o", false, "offline mode 离线模式") + installRedisCmd.Flags().StringVarP(&redisPort, "port", "p", "6379", "Redis listen port 监听端口") + installRedisCmd.Flags().StringVarP(&redisBindIP, "bind", "b", "0.0.0.0", "Redis bind address 监听地址") + installRedisCmd.Flags().StringVarP(&redisPassword, "password", "a", "redis", "Redis password 密码") + installRedisCmd.Flags().StringVarP(&redisDataDir, "data", "d", "/var/lib/redis", "Redis persistent directory 持久化目录") + installRedisCmd.Flags().StringVarP(&redisLogDir, "log-file", "", "/var/log/redis", "Redis logfile directory 日志目录") + installRedisCmd.Flags().StringVarP(&filePath, "file", "f", "", "docker-x-x-x.tgz path 安装包路径") + installRedisCmd.Flags().StringVarP(&redisBinaryPath, "binary-path", "", "/usr/bin/", "redis-* binary file path 二进制文件路径") installDockerCmd.Flags().StringVarP(&filePath, "file", "f", "", "redis-x-x-x.tar.gz path") installDockerCmd.Flags().BoolVarP(&offline, "offline", "o", false, "offline mode") @@ -50,13 +50,16 @@ func init() { // install命令 var installCmd = &cobra.Command{ Use: "install [OPTIONS] [flags]", - Short: "install some soft through easyctl", + Short: "install soft through easyctl", Example: "\neasyctl install docker" + "\neasyctl install nginx", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { + return parseCommand(cmd, args, installValidArgs) + }, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return installValidArgs, cobra.ShellCompDirectiveNoFileComp }, - ValidArgs: installValidArgs, - Args: cobra.ExactValidArgs(1), } // install docker命令 @@ -66,7 +69,6 @@ var installDockerCmd = &cobra.Command{ Example: "\neasyctl install docker 在线安装docker" + "\neasyctl install docker --offline --file=./docker-19.03.9.tgz 离线安装docker", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("dddddddddd", !offline) if !offline { installDockerOnline() } else { diff --git a/cmd/root.go b/cmd/root.go index c0aeaf3b..d08407d8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,9 +1,11 @@ package cmd import ( + "easyctl/util" "fmt" "github.com/spf13/cobra" "os" + "strings" ) var rootCmd = &cobra.Command{ @@ -37,3 +39,43 @@ func hasFlags(cmd *cobra.Command, args []string) bool { ) return false } + +func findSuggestions(c *cobra.Command, arg string) string { + if c.DisableSuggestions { + return "" + } + if c.SuggestionsMinimumDistance <= 0 { + c.SuggestionsMinimumDistance = 2 + } + suggestionsString := "" + if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 { + suggestionsString += "\n\nDid you mean this?\n" + for _, s := range suggestions { + suggestionsString += fmt.Sprintf("\t%v\n", s) + } + } + return suggestionsString +} + +func parseCommand(cmd *cobra.Command, args []string, validArgs []string) error { + //func parseCommand(cmd *cobra.Command,args[] string,validArgs []string,minArgNum int,maxArgNum int) error{ + + //if minArgNum > len(args) { + // return fmt.Errorf("requires at least %d arg(s), only received %d", minArgNum, len(args)) + //} + // + //if maxArgNum < len(args) { + // return fmt.Errorf("accepts at most %d arg(s), received %d", maxArgNum, len(args)) + //} + + for _, v := range validArgs { + validArgs = append(validArgs, strings.Split(v, "\t")[0]) + } + + for _, v := range args { + if !util.StringInSlice(v, validArgs) { + return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), findSuggestions(cmd, args[0])) + } + } + return nil +} diff --git a/sys/info.go b/sys/info.go index 03290d49..4c161b11 100644 --- a/sys/info.go +++ b/sys/info.go @@ -71,7 +71,7 @@ func init() { func (system *SystemInfo) loadOSReleaseContent() { //fmt.Println("获取操作系统版本信息...") // todo 优化获取os类型代码 - systemType, _ := util.ExecuteCmdAcceptResult("echo $OSTYPE") + systemType, _ := util.ExecuteCmdResult("echo $OSTYPE") system.OSVersion.OSType = systemType } diff --git a/util/execute.go b/util/execute.go index e89e3e99..1940358d 100644 --- a/util/execute.go +++ b/util/execute.go @@ -27,7 +27,7 @@ func ExecuteCmd(command string) (err error, result string) { func ExecuteCmdAcceptResult(command string) (result string, err error) { cmd := exec.Command("/bin/bash", "-c", command) - fmt.Printf("[shell] 执行语句:%s\n", command) + //fmt.Printf("[shell] 执行语句:%s\n", command) stderr, _ := cmd.StderrPipe() stdout, _ := cmd.StdoutPipe() if err := cmd.Start(); err != nil { @@ -60,3 +60,35 @@ func ExecuteCmdAcceptResult(command string) (result string, err error) { } return result, nil } + +func ExecuteCmdResult(command string) (result string, err error) { + cmd := exec.Command("/bin/bash", "-c", command) + //fmt.Printf("[shell] 执行语句:%s\n", command) + stderr, _ := cmd.StderrPipe() + stdout, _ := cmd.StdoutPipe() + if err := cmd.Start(); err != nil { + return "", err + } + // 正常日志 + logScan := bufio.NewScanner(stdout) + go func() { + for logScan.Scan() { + result = logScan.Text() + } + }() + // 错误日志 + errBuf := bytes.NewBufferString("") + scan := bufio.NewScanner(stderr) + for scan.Scan() { + s := scan.Text() + errBuf.WriteString(s) + errBuf.WriteString("\n") + } + // 等待命令执行完 + cmd.Wait() + if !cmd.ProcessState.Success() { + // 执行失败,返回错误信息 + return "", errors.New(errBuf.String()) + } + return result, nil +} diff --git a/util/slice.go b/util/slice.go new file mode 100644 index 00000000..1b7c0bc5 --- /dev/null +++ b/util/slice.go @@ -0,0 +1,10 @@ +package util + +func StringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +}