| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- //go:build !windows
- // +build !windows
- // Author: NiuJiuRu
- // Email: niujiuru@qq.com
- package shell
- import (
- "os"
- "path/filepath"
- "strings"
- "syscall"
- )
- type Executor struct {
- cwd string // 当前目录
- prevCwd string // 上次目录
- pg *processGroup // 执行命令的进程组
- }
- type PathError struct {
- Path string
- Info string
- }
- func (e *PathError) Error() string {
- return e.Info + ": " + e.Path
- }
- func NewExecutor() *Executor {
- return &Executor{cwd: "/"}
- }
- func (e *Executor) Exec(p ExecuteParams) (*ExecuteResult, error) {
- if e.isCD(p.Cmd) {
- dir, err := e.resolveCD(e.cwd, p.Cmd)
- if err != nil {
- return &ExecuteResult{
- Stderr: err.Error(),
- ExitCode: 1,
- }, nil
- }
- e.prevCwd = e.cwd
- e.cwd = dir
- return &ExecuteResult{ExitCode: 0, Cwd: e.cwd}, nil
- }
- if strings.TrimSpace(p.Cmd) == "pwd" {
- return &ExecuteResult{
- Stdout: e.cwd + "\n",
- ExitCode: 0,
- }, nil
- }
- p.Dir = e.cwd
- defer func() {
- e.pg = nil // 命令结束
- }()
- result, err := executeInternal(p, func(pg *processGroup) { e.pg = pg })
- if result != nil {
- result.Cwd = e.cwd
- }
- return result, err
- }
- func (e *Executor) isCD(cmd string) bool {
- s := strings.TrimSpace(cmd)
- return s == "cd" || strings.HasPrefix(s, "cd ")
- }
- func (e *Executor) resolveCD(cwd, cmd string) (string, error) {
- fields := strings.Fields(cmd)
- if len(fields) == 1 { // 只有"cd", 没有参数
- return "/", nil
- }
- path := fields[1]
- switch path {
- case "~":
- home := os.Getenv("HOME")
- if home == "" {
- home = "/"
- }
- return home, nil
- case "-":
- if e.prevCwd == "" {
- return cwd, nil
- }
- return e.prevCwd, nil
- }
- var target string
- if filepath.IsAbs(path) { // 绝对路径
- target = filepath.Clean(path)
- } else { // 相对路径
- target = filepath.Clean(filepath.Join(cwd, path))
- }
- info, err := os.Stat(target)
- if err != nil {
- if os.IsNotExist(err) {
- return "", &PathError{Path: target, Info: "directory not exist"}
- }
- return "", err
- }
- if !info.IsDir() {
- return "", &PathError{Path: target, Info: "not a directory"}
- }
- return target, nil
- }
- func (e *Executor) Interrupt() error {
- if e.pg == nil {
- return nil
- }
- return syscall.Kill(-e.pg.pgid, syscall.SIGINT) // 等价于 Ctrl+C
- }
|