executor.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package shell
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. "syscall"
  7. )
  8. type Executor struct {
  9. cwd string // 当前目录
  10. prevCwd string // 上次目录
  11. pg *processGroup // --进程组
  12. }
  13. type PathError struct {
  14. Path string
  15. Info string
  16. }
  17. func (e *PathError) Error() string {
  18. return e.Info + ": " + e.Path
  19. }
  20. func NewExecutor() *Executor {
  21. return &Executor{cwd: "/"}
  22. }
  23. func (e *Executor) Exec(p ExecuteParams) (*ExecuteResult, error) {
  24. if e.isCD(p.Cmd) {
  25. dir, err := e.resolveCD(e.cwd, p.Cmd)
  26. if err != nil {
  27. return &ExecuteResult{
  28. Stderr: err.Error(),
  29. ExitCode: 1,
  30. }, nil
  31. }
  32. e.prevCwd = e.cwd
  33. e.cwd = dir
  34. return &ExecuteResult{ExitCode: 0, Cwd: e.cwd}, nil
  35. }
  36. if strings.TrimSpace(p.Cmd) == "pwd" {
  37. return &ExecuteResult{
  38. Stdout: e.cwd + "\n",
  39. ExitCode: 0,
  40. }, nil
  41. }
  42. p.Dir = e.cwd
  43. defer func() {
  44. e.pg = nil // 命令结束
  45. }()
  46. result, err := executeInternal(p, func(pg *processGroup) { e.pg = pg })
  47. if result != nil {
  48. result.Cwd = e.cwd
  49. }
  50. return result, err
  51. }
  52. func (e *Executor) isCD(cmd string) bool {
  53. s := strings.TrimSpace(cmd)
  54. return s == "cd" || strings.HasPrefix(s, "cd ")
  55. }
  56. func (e *Executor) resolveCD(cwd, cmd string) (string, error) {
  57. fields := strings.Fields(cmd)
  58. if len(fields) == 1 { // 只有"cd", 没有参数
  59. return "/", nil
  60. }
  61. path := fields[1]
  62. switch path {
  63. case "~":
  64. home := os.Getenv("HOME")
  65. if home == "" {
  66. home = "/"
  67. }
  68. return home, nil
  69. case "-":
  70. if e.prevCwd == "" {
  71. return cwd, nil
  72. }
  73. return e.prevCwd, nil
  74. }
  75. var target string
  76. if filepath.IsAbs(path) { // 绝对路径
  77. target = filepath.Clean(path)
  78. } else { // 相对路径
  79. target = filepath.Clean(filepath.Join(cwd, path))
  80. }
  81. info, err := os.Stat(target)
  82. if err != nil {
  83. if os.IsNotExist(err) {
  84. return "", &PathError{Path: target, Info: "directory not exist"}
  85. }
  86. return "", err
  87. }
  88. if !info.IsDir() {
  89. return "", &PathError{Path: target, Info: "not a directory"}
  90. }
  91. return target, nil
  92. }
  93. func (e *Executor) Interrupt() error {
  94. if e.pg == nil {
  95. return nil
  96. }
  97. return syscall.Kill(-e.pg.pgid, syscall.SIGINT) // 等价于 Ctrl+C
  98. }