Added parallel computing
parent
8ca9e4b50c
commit
da1957a8c6
12
Readme.md
12
Readme.md
|
@ -21,10 +21,11 @@ import "os/exec"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := shelly.Exec(`
|
err := shelly.Exec(`
|
||||||
ls -larth
|
ls -larth &
|
||||||
pwd
|
pwd &
|
||||||
whoami
|
whoami
|
||||||
date`, &shelly.Opts{
|
date`, &shelly.Opts{
|
||||||
|
Await: false,
|
||||||
SetupProc: func(cmd *exec.Cmd) {
|
SetupProc: func(cmd *exec.Cmd) {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
@ -36,5 +37,8 @@ func main(){
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note: Commands may be connected by `;` and `\n`. In any case they will be executed sequentially.
|
Please note: Commands may be connected by `;` and `\n`. In any case they will be executed sequentially. Lines terminated
|
||||||
`&` will come later (pending implementation), and will allow processes to run in parallel.
|
by `&` will be executed in parallel.
|
||||||
|
|
||||||
|
In case the Await property is true, shelly will wait for all lines to be executed (including parallel ones), otherwise
|
||||||
|
it will return as soon as serial tasks are finished (if any)
|
117
lib.go
117
lib.go
|
@ -6,14 +6,11 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode int
|
type Mode int
|
||||||
|
|
||||||
type Opts struct {
|
|
||||||
SetupProc func(cmd *exec.Cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m Mode) String() string {
|
func (m Mode) String() string {
|
||||||
switch m {
|
switch m {
|
||||||
case MODE_NORMAL:
|
case MODE_NORMAL:
|
||||||
|
@ -36,6 +33,29 @@ const (
|
||||||
MODE_ESCAPE_STR
|
MODE_ESCAPE_STR
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LineType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LINETYPE_SERIAL LineType = iota
|
||||||
|
LINETYPE_PARALLEL
|
||||||
|
LINETYPE_COMMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
Await bool
|
||||||
|
SetupProc func(cmd *exec.Cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line represents a line to be executed by the shell.
|
||||||
|
// Can be separated by \n our ; - in case of serial execution. & will be used to identify parallel execution
|
||||||
|
type Line struct {
|
||||||
|
Tokens []string
|
||||||
|
LType LineType
|
||||||
|
}
|
||||||
|
|
||||||
// Tokens will break the string into tokens - It can be later organized in command lines and alikes
|
// Tokens will break the string into tokens - It can be later organized in command lines and alikes
|
||||||
func Tokens(str string) ([]string, error) {
|
func Tokens(str string) ([]string, error) {
|
||||||
params := make([]string, 0)
|
params := make([]string, 0)
|
||||||
|
@ -127,25 +147,47 @@ func Tokens(str string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lines will call Tokens first, and reorg them in executable lines
|
// Lines will call Tokens first, and reorg them in executable lines
|
||||||
func Lines(str string) ([][]string, error) {
|
func Lines(str string) ([]Line, error) {
|
||||||
ret := make([][]string, 0)
|
ret := make([]Line, 0)
|
||||||
tokens, err := Tokens(str)
|
tokens, err := Tokens(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
line := make([]string, 0)
|
line := Line{
|
||||||
|
Tokens: make([]string, 0),
|
||||||
|
LType: LINETYPE_SERIAL,
|
||||||
|
}
|
||||||
for _, tk := range tokens {
|
for _, tk := range tokens {
|
||||||
switch tk {
|
switch tk {
|
||||||
case "\n", "&&", ";":
|
case "\n", ";":
|
||||||
if len(line) > 0 {
|
if len(line.Tokens) > 0 {
|
||||||
|
if strings.HasPrefix(line.Tokens[0], "#") {
|
||||||
|
line.LType = LINETYPE_COMMENT
|
||||||
|
} else {
|
||||||
|
line.LType = LINETYPE_SERIAL
|
||||||
|
}
|
||||||
ret = append(ret, line)
|
ret = append(ret, line)
|
||||||
}
|
}
|
||||||
line = make([]string, 0)
|
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 = Line{
|
||||||
|
Tokens: make([]string, 0),
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
line = append(line, tk)
|
line.Tokens = append(line.Tokens, tk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(line) > 0 {
|
if len(line.Tokens) > 0 {
|
||||||
ret = append(ret, line)
|
ret = append(ret, line)
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
@ -153,27 +195,56 @@ func Lines(str string) ([][]string, error) {
|
||||||
|
|
||||||
// Exec will call Lines and execute one by one
|
// Exec will call Lines and execute one by one
|
||||||
func Exec(str string, opts ...*Opts) error {
|
func Exec(str string, opts ...*Opts) error {
|
||||||
|
|
||||||
|
var opt *Opts
|
||||||
|
if opts != nil && len(opts) > 0 {
|
||||||
|
opt = opts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.SetupProc == nil {
|
||||||
|
opt.SetupProc = func(cmd *exec.Cmd) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepCmd := func(l Line) *exec.Cmd {
|
||||||
|
cmd := exec.Command(l.Tokens[0], l.Tokens[1:]...)
|
||||||
|
cmd.Stdout = log.Writer()
|
||||||
|
cmd.Stderr = log.Writer()
|
||||||
|
opt.SetupProc(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
lines, err := Lines(str)
|
lines, err := Lines(str)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, l := range lines {
|
for _, l := range lines {
|
||||||
if len(l) < 1 {
|
|
||||||
|
switch l.LType {
|
||||||
|
case LINETYPE_COMMENT:
|
||||||
continue
|
continue
|
||||||
}
|
case LINETYPE_SERIAL:
|
||||||
if strings.HasPrefix(l[0], "#") {
|
cmd := prepCmd(l)
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd := exec.Command(l[0], l[1:]...)
|
|
||||||
if opts != nil && len(opts) > 0 {
|
|
||||||
opts[0].SetupProc(cmd)
|
|
||||||
}
|
|
||||||
cmd.Stdout = log.Writer()
|
|
||||||
cmd.Stderr = log.Writer()
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case LINETYPE_PARALLEL:
|
||||||
|
go func(l Line) {
|
||||||
|
cmd := prepCmd(l)
|
||||||
|
wg.Add(1)
|
||||||
|
err = cmd.Run()
|
||||||
|
wg.Done()
|
||||||
|
}(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.Await {
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -28,10 +28,11 @@ func TestLines(t *testing.T) {
|
||||||
|
|
||||||
func TestExec(t *testing.T) {
|
func TestExec(t *testing.T) {
|
||||||
err := Exec(`
|
err := Exec(`
|
||||||
ls -larth
|
ls -larth &
|
||||||
pwd
|
pwd &
|
||||||
whoami
|
whoami &
|
||||||
date`, &Opts{
|
date`, &Opts{
|
||||||
|
Await: false,
|
||||||
SetupProc: func(cmd *exec.Cmd) {
|
SetupProc: func(cmd *exec.Cmd) {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
@ -41,4 +42,6 @@ func TestExec(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("DONE")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue