executor.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  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. result, err := executeInternal(p, func(pg *processGroup) { e.pg = pg })
  37. if result != nil {
  38. result.Cwd = e.cwd
  39. }
  40. return result, err
  41. }
  42. func isCD(cmd string) bool {
  43. s := strings.TrimSpace(cmd)
  44. return s == "cd" || strings.HasPrefix(s, "cd ")
  45. }
  46. func resolveCD(cwd, cmd string) (string, error) {
  47. fields := strings.Fields(cmd)
  48. if len(fields) == 1 { // 只有"cd", 没有参数
  49. return "/", nil
  50. }
  51. path := fields[1]
  52. if filepath.IsAbs(path) { // 绝对路径
  53. return filepath.Clean(path), nil
  54. }
  55. return filepath.Clean(filepath.Join(cwd, path)), nil // 相对路径
  56. }
  57. func (e *Executor) Interrupt() error {
  58. if e.pg == nil {
  59. return nil
  60. }
  61. return syscall.Kill(-e.pg.pgid, syscall.SIGINT) // 等价于 Ctrl+C
  62. }