executor.go 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. package shell
  2. import (
  3. "path/filepath"
  4. "strings"
  5. "syscall"
  6. )
  7. type Executor struct {
  8. cwd string // 当前工作目录
  9. pg *processGroup // 当前前台进程
  10. }
  11. func NewExecutor() *Executor {
  12. return &Executor{cwd: "/"}
  13. }
  14. func (e *Executor) Exec(p ExecuteParams) (*ExecuteResult, error) {
  15. if isCD(p.Cmd) {
  16. dir, err := resolveCD(e.cwd, p.Cmd)
  17. if err != nil {
  18. return &ExecuteResult{
  19. Stderr: err.Error() + "\n",
  20. ExitCode: 1,
  21. }, nil
  22. }
  23. e.cwd = dir
  24. return &ExecuteResult{ExitCode: 0}, nil
  25. }
  26. if strings.TrimSpace(p.Cmd) == "pwd" {
  27. return &ExecuteResult{
  28. Stdout: e.cwd + "\n",
  29. ExitCode: 0,
  30. }, nil
  31. }
  32. p.Dir = e.cwd
  33. defer func() {
  34. e.pg = nil // 命令结束
  35. }()
  36. return executeInternal(p, func(pg *processGroup) { e.pg = pg })
  37. }
  38. func isCD(cmd string) bool {
  39. s := strings.TrimSpace(cmd)
  40. return s == "cd" || strings.HasPrefix(s, "cd ")
  41. }
  42. func resolveCD(cwd, cmd string) (string, error) {
  43. fields := strings.Fields(cmd)
  44. if len(fields) == 1 { // 只有"cd", 没有参数
  45. return "/", nil
  46. }
  47. path := fields[1]
  48. if filepath.IsAbs(path) { // 绝对路径
  49. return filepath.Clean(path), nil
  50. }
  51. return filepath.Clean(filepath.Join(cwd, path)), nil // 相对路径
  52. }
  53. func (e *Executor) Interrupt() error {
  54. if e.pg == nil {
  55. return nil
  56. }
  57. return syscall.Kill(-e.pg.pgid, syscall.SIGINT) // 等价于 Ctrl+C
  58. }