Added parallel computing V2

master
Paulo Simão 2021-10-25 18:44:30 -03:00
parent 2195674c08
commit dfdb8f0c50
3 changed files with 94 additions and 43 deletions

View File

@ -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)
}

116
lib.go
View File

@ -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
}
}

View File

@ -27,7 +27,7 @@ func TestLines(t *testing.T) {
}
func TestExec(t *testing.T) {
err := Exec(`
_, err := Exec(`
ls -larth &
pwd &
whoami &