shell_liner.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package shell
  2. import (
  3. "fmt"
  4. "github.com/chrislusf/seaweedfs/weed/util/grace"
  5. "io"
  6. "os"
  7. "path"
  8. "regexp"
  9. "sort"
  10. "strings"
  11. "github.com/peterh/liner"
  12. )
  13. var (
  14. line *liner.State
  15. historyPath = path.Join(os.TempDir(), "weed-shell")
  16. )
  17. func RunShell(options ShellOptions) {
  18. sort.Slice(Commands, func(i, j int) bool {
  19. return strings.Compare(Commands[i].Name(), Commands[j].Name()) < 0
  20. })
  21. line = liner.NewLiner()
  22. defer line.Close()
  23. grace.OnInterrupt(func() {
  24. line.Close()
  25. })
  26. line.SetCtrlCAborts(true)
  27. line.SetTabCompletionStyle(liner.TabPrints)
  28. setCompletionHandler()
  29. loadHistory()
  30. defer saveHistory()
  31. reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
  32. commandEnv := NewCommandEnv(options)
  33. go commandEnv.MasterClient.KeepConnectedToMaster()
  34. commandEnv.MasterClient.WaitUntilConnected()
  35. for {
  36. cmd, err := line.Prompt("> ")
  37. if err != nil {
  38. if err != io.EOF {
  39. fmt.Printf("%v\n", err)
  40. }
  41. return
  42. }
  43. for _, c := range strings.Split(cmd, ";") {
  44. if processEachCmd(reg, c, commandEnv) {
  45. return
  46. }
  47. }
  48. }
  49. }
  50. func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
  51. cmds := reg.FindAllString(cmd, -1)
  52. if len(cmds) == 0 {
  53. return false
  54. } else {
  55. line.AppendHistory(cmd)
  56. args := make([]string, len(cmds[1:]))
  57. for i := range args {
  58. args[i] = strings.Trim(string(cmds[1+i]), "\"'")
  59. }
  60. cmd := cmds[0]
  61. if cmd == "help" || cmd == "?" {
  62. printHelp(cmds)
  63. } else if cmd == "exit" || cmd == "quit" {
  64. return true
  65. } else {
  66. foundCommand := false
  67. for _, c := range Commands {
  68. if c.Name() == cmd || c.Name() == "fs."+cmd {
  69. if err := c.Do(args, commandEnv, os.Stdout); err != nil {
  70. fmt.Fprintf(os.Stderr, "error: %v\n", err)
  71. }
  72. foundCommand = true
  73. }
  74. }
  75. if !foundCommand {
  76. fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
  77. }
  78. }
  79. }
  80. return false
  81. }
  82. func printGenericHelp() {
  83. msg :=
  84. `Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
  85. `
  86. fmt.Print(msg)
  87. for _, c := range Commands {
  88. helpTexts := strings.SplitN(c.Help(), "\n", 2)
  89. fmt.Printf(" %-30s\t# %s \n", c.Name(), helpTexts[0])
  90. }
  91. }
  92. func printHelp(cmds []string) {
  93. args := cmds[1:]
  94. if len(args) == 0 {
  95. printGenericHelp()
  96. } else if len(args) > 1 {
  97. fmt.Println()
  98. } else {
  99. cmd := strings.ToLower(args[0])
  100. for _, c := range Commands {
  101. if c.Name() == cmd {
  102. fmt.Printf(" %s\t# %s\n", c.Name(), c.Help())
  103. }
  104. }
  105. }
  106. }
  107. func setCompletionHandler() {
  108. line.SetCompleter(func(line string) (c []string) {
  109. for _, i := range Commands {
  110. if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
  111. c = append(c, i.Name())
  112. }
  113. }
  114. return
  115. })
  116. }
  117. func loadHistory() {
  118. if f, err := os.Open(historyPath); err == nil {
  119. line.ReadHistory(f)
  120. f.Close()
  121. }
  122. }
  123. func saveHistory() {
  124. if f, err := os.Create(historyPath); err != nil {
  125. fmt.Printf("Error creating history file: %v\n", err)
  126. } else {
  127. if _, err = line.WriteHistory(f); err != nil {
  128. fmt.Printf("Error writing history file: %v\n", err)
  129. }
  130. f.Close()
  131. }
  132. }