executor.go 2.2 KB

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