diff --git a/Readme.md b/Readme.md index 24d9a5b..d8e0826 100644 --- a/Readme.md +++ b/Readme.md @@ -20,17 +20,20 @@ import "os" import "os/exec" func main() { - err := shelly.Exec(` + _, err := shelly.Exec(` ls -larth & pwd & whoami - date`, &shelly.Opts{ - Await: false, - SetupProc: func(cmd *exec.Cmd) { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - }, - }) + date`, + &shelly.Opts{ + Await: false, + Wd: ".", + Debug: true, + SetupProc: func(cmd *exec.Cmd) { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + }, + }) if err != nil { panic(err) } diff --git a/lib.go b/lib.go index d5da228..e20117b 100644 --- a/lib.go +++ b/lib.go @@ -4,10 +4,11 @@ import ( "errors" "fmt" "log" + "os" "os/exec" + "path/filepath" "runtime" "strings" - "sync" ) type Mode int @@ -40,13 +41,16 @@ const ( LINETYPE_SERIAL LineType = iota LINETYPE_PARALLEL LINETYPE_COMMENT + LINETYPE_CHANGEWD ) // Opts tells Exec how to behave // Opts.Await will inform whether its call should wait for all parallel started jobs to finish // Opts.SetupProc will customize the processes before execution - Eg.: setting out and err type Opts struct { + Debug bool Await bool + Wd string SetupProc func(cmd *exec.Cmd) } @@ -158,28 +162,32 @@ func Lines(str string) ([]Line, error) { Tokens: make([]string, 0), LType: LINETYPE_SERIAL, } + + addLine := func(line Line) { + if strings.HasPrefix(line.Tokens[0], "#") { + line.LType = LINETYPE_COMMENT + } + + if line.Tokens[0] == "cd" { + line.LType = LINETYPE_CHANGEWD + } + + ret = append(ret, line) + } + for _, tk := range tokens { switch tk { - case "\n", ";": + case "\n", ";", "&&": if len(line.Tokens) > 0 { - if strings.HasPrefix(line.Tokens[0], "#") { - line.LType = LINETYPE_COMMENT - } else { - line.LType = LINETYPE_SERIAL - } - ret = append(ret, line) + addLine(line) } line = Line{ Tokens: make([]string, 0), } case "&": if len(line.Tokens) > 0 { - if strings.HasPrefix(line.Tokens[0], "#") { - line.LType = LINETYPE_COMMENT - } else { - line.LType = LINETYPE_PARALLEL - } - ret = append(ret, line) + line.LType = LINETYPE_PARALLEL + addLine(line) } line = Line{ Tokens: make([]string, 0), @@ -189,15 +197,26 @@ func Lines(str string) ([]Line, error) { } } if len(line.Tokens) > 0 { - ret = append(ret, line) + addLine(line) } return ret, nil } // Exec will call Lines and execute one by one -func Exec(str string, opts ...*Opts) error { +func Exec(str string, opts ...*Opts) ([]*exec.Cmd, error) { + wd, err := os.Getwd() - var opt *Opts + if err != nil { + return nil, err + } + ret := make([]*exec.Cmd, 0) + opt := &Opts{ + Await: false, + Wd: wd, + SetupProc: func(cmd *exec.Cmd) { + + }, + } if opts != nil && len(opts) > 0 { opt = opts[0] } @@ -208,47 +227,74 @@ func Exec(str string, opts ...*Opts) error { } } + cmdwd := opt.Wd + prepCmd := func(l Line) *exec.Cmd { cmd := exec.Command(l.Tokens[0], l.Tokens[1:]...) cmd.Stdout = log.Writer() cmd.Stderr = log.Writer() + cmd.Dir = cmdwd opt.SetupProc(cmd) + ret = append(ret, cmd) return cmd } - wg := sync.WaitGroup{} lines, err := Lines(str) if err != nil { - return err + return ret, err } for _, l := range lines { switch l.LType { case LINETYPE_COMMENT: continue + case LINETYPE_CHANGEWD: + + if filepath.IsAbs(l.Tokens[1]) { + cmdwd = l.Tokens[1] + } else { + cmdwd, err = filepath.Abs(filepath.Join(wd, l.Tokens[1])) + if err != nil { + return nil, err + } + } + log.Printf("CMDWD NOW IS: %s", cmdwd) + continue case LINETYPE_SERIAL: cmd := prepCmd(l) + if opt.Debug { + log.Printf("Running %s from dir %s with params %v", cmd.Path, cmd.Dir, cmd.Args) + } err = cmd.Run() + if opt.Debug { + if err != nil { + log.Printf("Error running: %s: %s", cmd.Path, err.Error()) + } + } if err != nil { - return err + return ret, err } case LINETYPE_PARALLEL: - go func(l Line) { - cmd := prepCmd(l) - wg.Add(1) - err = cmd.Run() - wg.Done() - }(l) + cmd := prepCmd(l) + + if opt.Debug { + log.Printf("Running %s from dir %s with params %v", cmd.Path, cmd.Dir, cmd.Args) + } + err = cmd.Start() + if opt.Debug { + if err != nil { + log.Printf("Error running: %s: %s", cmd.Path, err.Error()) + } + } + if err != nil { + return nil, err + } + } - } - if opt.Await { - wg.Wait() - } - - return nil + return ret, err } @@ -256,9 +302,11 @@ func Exec(str string, opts ...*Opts) error { func Kill(p int) error { switch runtime.GOOS { case "windows": - return Exec(fmt.Sprintf("taskkill /T /F /PID %d", p)) + _, err := Exec(fmt.Sprintf("taskkill /T /F /PID %d", p)) + return err default: - return Exec(fmt.Sprintf("kill -9 %d", p)) + _, err := Exec(fmt.Sprintf("kill -9 %d", p)) + return err } } diff --git a/lib_test.go b/lib_test.go index e9964a2..f4a96f3 100644 --- a/lib_test.go +++ b/lib_test.go @@ -27,7 +27,7 @@ func TestLines(t *testing.T) { } func TestExec(t *testing.T) { - err := Exec(` + _, err := Exec(` ls -larth & pwd & whoami &