shell_liner.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. ctx := context.Background()
  40. go commandEnv.MasterClient.KeepConnectedToMaster(ctx)
  41. commandEnv.MasterClient.WaitUntilConnected(ctx)
  42. if commandEnv.option.FilerAddress == "" {
  43. var filers []pb.ServerAddress
  44. commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
  45. resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
  46. ClientType: cluster.FilerType,
  47. FilerGroup: *options.FilerGroup,
  48. })
  49. if err != nil {
  50. return err
  51. }
  52. for _, clusterNode := range resp.ClusterNodes {
  53. filers = append(filers, pb.ServerAddress(clusterNode.Address))
  54. }
  55. return nil
  56. })
  57. fmt.Printf("master: %s ", *options.Masters)
  58. if len(filers) > 0 {
  59. fmt.Printf("filers: %v", filers)
  60. commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
  61. }
  62. fmt.Println()
  63. }
  64. for {
  65. cmd, err := line.Prompt("> ")
  66. if err != nil {
  67. if err != io.EOF {
  68. fmt.Printf("%v\n", err)
  69. }
  70. return
  71. }
  72. for _, c := range util.StringSplit(cmd, ";") {
  73. if processEachCmd(reg, c, commandEnv) {
  74. return
  75. }
  76. }
  77. }
  78. }
  79. func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
  80. cmds := reg.FindAllString(cmd, -1)
  81. line.AppendHistory(cmd)
  82. if len(cmds) == 0 {
  83. return false
  84. } else {
  85. args := make([]string, len(cmds[1:]))
  86. for i := range args {
  87. args[i] = strings.Trim(string(cmds[1+i]), "\"'")
  88. }
  89. cmd := cmds[0]
  90. if cmd == "help" || cmd == "?" {
  91. printHelp(cmds)
  92. } else if cmd == "exit" || cmd == "quit" {
  93. return true
  94. } else {
  95. foundCommand := false
  96. for _, c := range Commands {
  97. if c.Name() == cmd || c.Name() == "fs."+cmd {
  98. if err := c.Do(args, commandEnv, os.Stdout); err != nil {
  99. fmt.Fprintf(os.Stderr, "error: %v\n", err)
  100. }
  101. foundCommand = true
  102. }
  103. }
  104. if !foundCommand {
  105. fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
  106. }
  107. }
  108. }
  109. return false
  110. }
  111. func printGenericHelp() {
  112. msg :=
  113. `Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
  114. `
  115. fmt.Print(msg)
  116. for _, c := range Commands {
  117. helpTexts := strings.SplitN(c.Help(), "\n", 2)
  118. fmt.Printf(" %-30s\t# %s \n", c.Name(), helpTexts[0])
  119. }
  120. }
  121. func printHelp(cmds []string) {
  122. args := cmds[1:]
  123. if len(args) == 0 {
  124. printGenericHelp()
  125. } else if len(args) > 1 {
  126. fmt.Println()
  127. } else {
  128. cmd := strings.ToLower(args[0])
  129. for _, c := range Commands {
  130. if strings.ToLower(c.Name()) == cmd {
  131. fmt.Printf(" %s\t# %s\n", c.Name(), c.Help())
  132. }
  133. }
  134. }
  135. }
  136. func setCompletionHandler() {
  137. line.SetCompleter(func(line string) (c []string) {
  138. for _, i := range Commands {
  139. if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
  140. c = append(c, i.Name())
  141. }
  142. }
  143. return
  144. })
  145. }
  146. func loadHistory() {
  147. if f, err := os.Open(historyPath); err == nil {
  148. line.ReadHistory(f)
  149. f.Close()
  150. }
  151. }
  152. func saveHistory() {
  153. if f, err := os.Create(historyPath); err != nil {
  154. fmt.Printf("Error creating history file: %v\n", err)
  155. } else {
  156. if _, err = line.WriteHistory(f); err != nil {
  157. fmt.Printf("Error writing history file: %v\n", err)
  158. }
  159. f.Close()
  160. }
  161. }