shell_liner.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/seaweedfs/seaweedfs/weed/cluster"
  6. "github.com/seaweedfs/seaweedfs/weed/pb"
  7. "github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
  8. "github.com/seaweedfs/seaweedfs/weed/util"
  9. "github.com/seaweedfs/seaweedfs/weed/util/grace"
  10. "golang.org/x/exp/slices"
  11. "io"
  12. "math/rand"
  13. "os"
  14. "path"
  15. "regexp"
  16. "strings"
  17. "github.com/peterh/liner"
  18. )
  19. var (
  20. line *liner.State
  21. historyPath = path.Join(os.TempDir(), "weed-shell")
  22. )
  23. func RunShell(options ShellOptions) {
  24. slices.SortFunc(Commands, func(a, b command) int {
  25. return strings.Compare(a.Name(), b.Name())
  26. })
  27. line = liner.NewLiner()
  28. defer line.Close()
  29. grace.OnInterrupt(func() {
  30. line.Close()
  31. })
  32. line.SetCtrlCAborts(true)
  33. line.SetTabCompletionStyle(liner.TabPrints)
  34. setCompletionHandler()
  35. loadHistory()
  36. defer saveHistory()
  37. reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
  38. commandEnv := NewCommandEnv(&options)
  39. go commandEnv.MasterClient.KeepConnectedToMaster()
  40. commandEnv.MasterClient.WaitUntilConnected()
  41. if commandEnv.option.FilerAddress == "" {
  42. var filers []pb.ServerAddress
  43. commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
  44. resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
  45. ClientType: cluster.FilerType,
  46. FilerGroup: *options.FilerGroup,
  47. })
  48. if err != nil {
  49. return err
  50. }
  51. for _, clusterNode := range resp.ClusterNodes {
  52. filers = append(filers, pb.ServerAddress(clusterNode.Address))
  53. }
  54. return nil
  55. })
  56. fmt.Printf("master: %s ", *options.Masters)
  57. if len(filers) > 0 {
  58. fmt.Printf("filers: %v", filers)
  59. commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
  60. }
  61. fmt.Println()
  62. }
  63. for {
  64. cmd, err := line.Prompt("> ")
  65. if err != nil {
  66. if err != io.EOF {
  67. fmt.Printf("%v\n", err)
  68. }
  69. return
  70. }
  71. for _, c := range util.StringSplit(cmd, ";") {
  72. if processEachCmd(reg, c, commandEnv) {
  73. return
  74. }
  75. }
  76. }
  77. }
  78. func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
  79. cmds := reg.FindAllString(cmd, -1)
  80. line.AppendHistory(cmd)
  81. if len(cmds) == 0 {
  82. return false
  83. } else {
  84. args := make([]string, len(cmds[1:]))
  85. for i := range args {
  86. args[i] = strings.Trim(string(cmds[1+i]), "\"'")
  87. }
  88. cmd := cmds[0]
  89. if cmd == "help" || cmd == "?" {
  90. printHelp(cmds)
  91. } else if cmd == "exit" || cmd == "quit" {
  92. return true
  93. } else {
  94. foundCommand := false
  95. for _, c := range Commands {
  96. if c.Name() == cmd || c.Name() == "fs."+cmd {
  97. if err := c.Do(args, commandEnv, os.Stdout); err != nil {
  98. fmt.Fprintf(os.Stderr, "error: %v\n", err)
  99. }
  100. foundCommand = true
  101. }
  102. }
  103. if !foundCommand {
  104. fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
  105. }
  106. }
  107. }
  108. return false
  109. }
  110. func printGenericHelp() {
  111. msg :=
  112. `Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
  113. `
  114. fmt.Print(msg)
  115. for _, c := range Commands {
  116. helpTexts := strings.SplitN(c.Help(), "\n", 2)
  117. fmt.Printf(" %-30s\t# %s \n", c.Name(), helpTexts[0])
  118. }
  119. }
  120. func printHelp(cmds []string) {
  121. args := cmds[1:]
  122. if len(args) == 0 {
  123. printGenericHelp()
  124. } else if len(args) > 1 {
  125. fmt.Println()
  126. } else {
  127. cmd := strings.ToLower(args[0])
  128. for _, c := range Commands {
  129. if strings.ToLower(c.Name()) == cmd {
  130. fmt.Printf(" %s\t# %s\n", c.Name(), c.Help())
  131. }
  132. }
  133. }
  134. }
  135. func setCompletionHandler() {
  136. line.SetCompleter(func(line string) (c []string) {
  137. for _, i := range Commands {
  138. if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
  139. c = append(c, i.Name())
  140. }
  141. }
  142. return
  143. })
  144. }
  145. func loadHistory() {
  146. if f, err := os.Open(historyPath); err == nil {
  147. line.ReadHistory(f)
  148. f.Close()
  149. }
  150. }
  151. func saveHistory() {
  152. if f, err := os.Create(historyPath); err != nil {
  153. fmt.Printf("Error creating history file: %v\n", err)
  154. } else {
  155. if _, err = line.WriteHistory(f); err != nil {
  156. fmt.Printf("Error writing history file: %v\n", err)
  157. }
  158. f.Close()
  159. }
  160. }