1st ver
commit
8ca9e4b50c
|
@ -0,0 +1 @@
|
|||
.idea
|
|
@ -0,0 +1,40 @@
|
|||
# Shelly
|
||||
|
||||
An abstraction for shell directly in GO
|
||||
|
||||
## Goal
|
||||
|
||||
Simplify process spawning and execution on top of exec.Command function.
|
||||
|
||||
The proposal is: use a string based approach for launching other process - as if you were calling a shell, but instead,
|
||||
its go directly executing your commands.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import "go.digitalcircle.com.br/open/shelly"
|
||||
import "os"
|
||||
import "os/exec"
|
||||
|
||||
func main(){
|
||||
err := shelly.Exec(`
|
||||
ls -larth
|
||||
pwd
|
||||
whoami
|
||||
date`, &shelly.Opts{
|
||||
SetupProc: func(cmd *exec.Cmd) {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please note: Commands may be connected by `;` and `\n`. In any case they will be executed sequentially.
|
||||
`&` will come later (pending implementation), and will allow processes to run in parallel.
|
|
@ -0,0 +1,181 @@
|
|||
package shelly
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Mode int
|
||||
|
||||
type Opts struct {
|
||||
SetupProc func(cmd *exec.Cmd)
|
||||
}
|
||||
|
||||
func (m Mode) String() string {
|
||||
switch m {
|
||||
case MODE_NORMAL:
|
||||
return "NORMAL"
|
||||
case MODE_STR:
|
||||
return "STR"
|
||||
case MODE_ESCAPE:
|
||||
return "ESCAPE"
|
||||
case MODE_ESCAPE_STR:
|
||||
return "ESCAPE_STR"
|
||||
}
|
||||
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
const (
|
||||
MODE_NORMAL Mode = iota
|
||||
MODE_STR
|
||||
MODE_ESCAPE
|
||||
MODE_ESCAPE_STR
|
||||
)
|
||||
|
||||
// Tokens will break the string into tokens - It can be later organized in command lines and alikes
|
||||
func Tokens(str string) ([]string, error) {
|
||||
params := make([]string, 0)
|
||||
sb := strings.Builder{}
|
||||
mode := MODE_NORMAL
|
||||
for i, a := range str {
|
||||
switch a {
|
||||
case ' ', '\t', '\r':
|
||||
switch mode {
|
||||
case MODE_NORMAL:
|
||||
if sb.Len() > 0 {
|
||||
params = append(params, sb.String())
|
||||
}
|
||||
sb = strings.Builder{}
|
||||
case MODE_STR:
|
||||
sb.WriteRune(a)
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Error at char %d - cant add space here. Mode: %s", i, mode.String()))
|
||||
}
|
||||
case '\n':
|
||||
switch mode {
|
||||
case MODE_NORMAL:
|
||||
if sb.Len() > 0 {
|
||||
params = append(params, sb.String())
|
||||
}
|
||||
params = append(params, "\n")
|
||||
sb = strings.Builder{}
|
||||
case MODE_STR:
|
||||
sb.WriteRune(a)
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Error at char %d - cant add new line here. Mode: %s", i, mode.String()))
|
||||
}
|
||||
case '"':
|
||||
switch mode {
|
||||
case MODE_NORMAL:
|
||||
if sb.Len() > 0 {
|
||||
params = append(params, sb.String())
|
||||
}
|
||||
sb = strings.Builder{}
|
||||
mode = MODE_STR
|
||||
case MODE_STR:
|
||||
params = append(params, sb.String())
|
||||
sb = strings.Builder{}
|
||||
mode = MODE_NORMAL
|
||||
case MODE_ESCAPE:
|
||||
sb.WriteRune(a)
|
||||
mode = MODE_NORMAL
|
||||
case MODE_ESCAPE_STR:
|
||||
sb.WriteRune(a)
|
||||
mode = MODE_STR
|
||||
}
|
||||
case '\\':
|
||||
switch mode {
|
||||
case MODE_NORMAL:
|
||||
mode = MODE_ESCAPE
|
||||
case MODE_STR:
|
||||
mode = MODE_ESCAPE_STR
|
||||
case MODE_ESCAPE:
|
||||
sb.WriteString("\\")
|
||||
mode = MODE_NORMAL
|
||||
case MODE_ESCAPE_STR:
|
||||
sb.WriteString("\\")
|
||||
mode = MODE_STR
|
||||
}
|
||||
default:
|
||||
switch mode {
|
||||
case MODE_NORMAL:
|
||||
sb.WriteRune(a)
|
||||
case MODE_STR:
|
||||
sb.WriteRune(a)
|
||||
case MODE_ESCAPE:
|
||||
sb.WriteString("\\" + string(a))
|
||||
mode = MODE_NORMAL
|
||||
case MODE_ESCAPE_STR:
|
||||
sb.WriteString("\\" + string(a))
|
||||
mode = MODE_STR
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if mode != MODE_NORMAL {
|
||||
return nil, errors.New("Unterminated String")
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
params = append(params, sb.String())
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Lines will call Tokens first, and reorg them in executable lines
|
||||
func Lines(str string) ([][]string, error) {
|
||||
ret := make([][]string, 0)
|
||||
tokens, err := Tokens(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line := make([]string, 0)
|
||||
for _, tk := range tokens {
|
||||
switch tk {
|
||||
case "\n", "&&", ";":
|
||||
if len(line) > 0 {
|
||||
ret = append(ret, line)
|
||||
}
|
||||
line = make([]string, 0)
|
||||
default:
|
||||
line = append(line, tk)
|
||||
}
|
||||
}
|
||||
if len(line) > 0 {
|
||||
ret = append(ret, line)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Exec will call Lines and execute one by one
|
||||
func Exec(str string, opts ...*Opts) error {
|
||||
lines, err := Lines(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range lines {
|
||||
if len(l) < 1 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(l[0], "#") {
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package shelly
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLines(t *testing.T) {
|
||||
lines, err := Lines("; \";\" ;;")
|
||||
//Lines("a\nb\\\\nc")
|
||||
// Lines(`
|
||||
//"A STR" 1 2 "a b" 1.23 \n &
|
||||
//"STR \\ with escape \n chars \t" 1 234 true
|
||||
//ls -larth
|
||||
//pwd
|
||||
//whoami
|
||||
//date`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, l := range lines {
|
||||
log.Printf("%#v", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
err := Exec(`
|
||||
ls -larth
|
||||
pwd
|
||||
whoami
|
||||
date`, &Opts{
|
||||
SetupProc: func(cmd *exec.Cmd) {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue