Add all
This commit is contained in:
4
go.mod
Normal file
4
go.mod
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module gitea.cmp167.cloudns.ph/cmp167/cmg-exec1
|
||||||
|
|
||||||
|
go 1.20.0
|
||||||
|
|
228
main.go
Normal file
228
main.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
|
||||||
|
package CmpExec1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"os/exec"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
"bytes"
|
||||||
|
"syscall"
|
||||||
|
"strings"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CmpExec1Struct struct {
|
||||||
|
C chan bool
|
||||||
|
Path string
|
||||||
|
Args []string
|
||||||
|
Error error
|
||||||
|
ExitCode int
|
||||||
|
StdOut []string
|
||||||
|
StdErr []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CmpExec1GroundStruct struct {
|
||||||
|
Background bool
|
||||||
|
LimitTime time.Duration
|
||||||
|
StdOutFunc func (string)
|
||||||
|
StdErrFunc func (string)
|
||||||
|
CommandArg []string
|
||||||
|
|
||||||
|
CtxCancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GetExitCode(cmd *exec.Cmd, ret *CmpExec1Struct) (*CmpExec1Struct) {
|
||||||
|
if ret.Error == nil {
|
||||||
|
ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
|
||||||
|
ret.ExitCode = ws.ExitStatus()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
exitError, ok := ret.Error.(*exec.ExitError)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
ws := exitError.Sys().(syscall.WaitStatus)
|
||||||
|
ret.ExitCode = ws.ExitStatus()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will happen (in OSX) if `name` is not available in $PATH,
|
||||||
|
// in this situation, exit code could not be get, and stderr will be
|
||||||
|
// empty string very likely, so we use the default fail code, and format err
|
||||||
|
// to string and set to stderr
|
||||||
|
|
||||||
|
// Wrap errror !!!
|
||||||
|
log.Println("Could not get exit code for failed program", ret.Path)
|
||||||
|
ret.ExitCode = 255
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1Simple(cma ...string) (*CmpExec1Struct) {
|
||||||
|
ret := &CmpExec1Struct{}
|
||||||
|
|
||||||
|
ret.Path, ret.Error = exec.LookPath(cma[0])
|
||||||
|
if ret.Error != nil {
|
||||||
|
ret.ExitCode = 127
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Args = cma[1:]
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
|
||||||
|
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, ret.Path, ret.Args...)
|
||||||
|
|
||||||
|
var bbStdOut, bbStdErr bytes.Buffer
|
||||||
|
|
||||||
|
cmd.Stdout = &bbStdOut
|
||||||
|
cmd.Stderr = &bbStdErr
|
||||||
|
|
||||||
|
ret.Error = cmd.Run()
|
||||||
|
|
||||||
|
ret.StdOut = strings.Split(bbStdOut.String(), "\n")
|
||||||
|
ret.StdErr = strings.Split(bbStdErr.String(), "\n")
|
||||||
|
|
||||||
|
return Exec1GetExitCode(cmd, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1OutCatch(SubjIn *CmpExec1GroundStruct, SubjOut *CmpExec1Struct, StreamNum int, r io.Reader) {
|
||||||
|
buff := make([]byte, 1024, 1024)
|
||||||
|
var tail string
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := r.Read(buff[:])
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
d := buff[:n]
|
||||||
|
|
||||||
|
strList := strings.Split(string(d), "\n")
|
||||||
|
|
||||||
|
strCnt := len(strList)
|
||||||
|
|
||||||
|
prcLen := 0
|
||||||
|
|
||||||
|
for i := 0; i < strCnt - 1; i++ {
|
||||||
|
strItem := strList[i]
|
||||||
|
|
||||||
|
if i == 0 && len(tail) > 0 {
|
||||||
|
strItem = tail + strList[i]
|
||||||
|
tail = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if StreamNum == 1 {
|
||||||
|
SubjIn.StdOutFunc(strItem)
|
||||||
|
} else if StreamNum == 2 {
|
||||||
|
SubjIn.StdErrFunc(strItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
prcLen += len(strList[i]) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var strLast string
|
||||||
|
|
||||||
|
if strCnt > 0 {
|
||||||
|
strLast = strList[strCnt - 1]
|
||||||
|
} else {
|
||||||
|
strLast = strList[strCnt]
|
||||||
|
}
|
||||||
|
|
||||||
|
prcLenZ := prcLen + len(strLast)
|
||||||
|
|
||||||
|
if prcLenZ != 0 {
|
||||||
|
if string(buff[prcLenZ-1:prcLenZ]) != "\n" {
|
||||||
|
tail = string(buff[prcLen:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Read returns io.EOF at the end of file, which is not an error for us
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
SubjOut.Error = err
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Exec1Wait(cmd *exec.Cmd, stdoutIn io.ReadCloser, stderrIn io.ReadCloser, opt *CmpExec1GroundStruct, ret *CmpExec1Struct) {
|
||||||
|
if opt.Background {
|
||||||
|
defer opt.CtxCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
Exec1OutCatch(opt, ret, 1, stdoutIn)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
Exec1OutCatch(opt, ret, 2, stderrIn)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
ret.Error = cmd.Wait()
|
||||||
|
|
||||||
|
Exec1GetExitCode(cmd, ret)
|
||||||
|
|
||||||
|
if ret.C != nil {
|
||||||
|
ret.C <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1Ground(opt *CmpExec1GroundStruct) (*CmpExec1Struct) {
|
||||||
|
ret := &CmpExec1Struct{}
|
||||||
|
|
||||||
|
ret.Path, ret.Error = exec.LookPath(opt.CommandArg[0])
|
||||||
|
if ret.Error != nil {
|
||||||
|
ret.ExitCode = 127
|
||||||
|
if ret.C != nil {
|
||||||
|
ret.C <- true
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Args = opt.CommandArg[1:]
|
||||||
|
|
||||||
|
if opt.Background {
|
||||||
|
ret.C = make(chan bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
|
if opt.LimitTime != 0 {
|
||||||
|
var ctx context.Context
|
||||||
|
ctx, opt.CtxCancel = context.WithTimeout(context.Background(), opt.LimitTime * time.Second)
|
||||||
|
cmd = exec.CommandContext(ctx, ret.Path, ret.Args...)
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command(ret.Path, ret.Args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdoutIn, _ := cmd.StdoutPipe()
|
||||||
|
stderrIn, _ := cmd.StderrPipe()
|
||||||
|
|
||||||
|
ret.Error = cmd.Start()
|
||||||
|
|
||||||
|
if opt.Background {
|
||||||
|
go Exec1Wait(cmd, stdoutIn, stderrIn, opt, ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
Exec1Wait(cmd, stdoutIn, stderrIn, opt, ret)
|
||||||
|
return ret
|
||||||
|
}
|
135
testGround.go
Normal file
135
testGround.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
package CmpExec1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Exec1GroundTestStdOutFunc(s string) {
|
||||||
|
if s == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("StdOut", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestStdErrFunc(s string) {
|
||||||
|
if s == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("StdErr", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTest(opt *CmpExec1GroundStruct) {
|
||||||
|
ret := Exec1Ground(opt)
|
||||||
|
|
||||||
|
Exec1Print(ret)
|
||||||
|
|
||||||
|
if ret.Error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.Background {
|
||||||
|
log.Println("Wait channel")
|
||||||
|
<- ret.C
|
||||||
|
|
||||||
|
Exec1Print(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestErrPath(bg bool) {
|
||||||
|
opt := &CmpExec1GroundStruct{
|
||||||
|
Background: bg,
|
||||||
|
LimitTime: 3000,
|
||||||
|
StdOutFunc: Exec1GroundTestStdOutFunc,
|
||||||
|
StdErrFunc: Exec1GroundTestStdErrFunc,
|
||||||
|
CommandArg: []string{
|
||||||
|
"./test.sh",
|
||||||
|
"-a",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Exec1GroundTest(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestErrParm(bg bool) {
|
||||||
|
opt := &CmpExec1GroundStruct{
|
||||||
|
Background: bg,
|
||||||
|
LimitTime: 3000,
|
||||||
|
StdOutFunc: Exec1GroundTestStdOutFunc,
|
||||||
|
StdErrFunc: Exec1GroundTestStdErrFunc,
|
||||||
|
CommandArg: []string{
|
||||||
|
"sleep",
|
||||||
|
"v",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Exec1GroundTest(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestTimeout(bg bool) {
|
||||||
|
opt := &CmpExec1GroundStruct{
|
||||||
|
Background: bg,
|
||||||
|
LimitTime: 2,
|
||||||
|
StdOutFunc: Exec1GroundTestStdOutFunc,
|
||||||
|
StdErrFunc: Exec1GroundTestStdErrFunc,
|
||||||
|
CommandArg: []string{
|
||||||
|
"sleep",
|
||||||
|
"4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Exec1GroundTest(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestSuccess(bg bool) {
|
||||||
|
opt := &CmpExec1GroundStruct{
|
||||||
|
Background: bg,
|
||||||
|
LimitTime: 3000,
|
||||||
|
StdOutFunc: Exec1GroundTestStdOutFunc,
|
||||||
|
StdErrFunc: Exec1GroundTestStdErrFunc,
|
||||||
|
CommandArg: []string{
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"sleep 1 ; echo ok ; sleep 1; echo ok",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Exec1GroundTest(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1GroundTestAll() {
|
||||||
|
log.Println("====", "Exec1GroundTestErrPath")
|
||||||
|
|
||||||
|
Exec1GroundTestErrPath(true)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestErrParm")
|
||||||
|
|
||||||
|
Exec1GroundTestErrParm(true)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestTimeout")
|
||||||
|
|
||||||
|
Exec1GroundTestTimeout(true)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestSuccess")
|
||||||
|
|
||||||
|
Exec1GroundTestSuccess(true)
|
||||||
|
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestErrPath")
|
||||||
|
|
||||||
|
Exec1GroundTestErrPath(false)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestErrParm")
|
||||||
|
|
||||||
|
Exec1GroundTestErrParm(false)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestTimeout")
|
||||||
|
|
||||||
|
Exec1GroundTestTimeout(false)
|
||||||
|
|
||||||
|
log.Println("====", "Exec1GroundTestSuccess")
|
||||||
|
|
||||||
|
Exec1GroundTestSuccess(false)
|
||||||
|
}
|
64
testSimple.go
Normal file
64
testSimple.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
package CmpExec1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Exec1Print(ret *CmpExec1Struct) {
|
||||||
|
log.Println("Path:" , ret.Path )
|
||||||
|
log.Println("Args:" , ret.Args )
|
||||||
|
log.Println("Error:" , ret.Error )
|
||||||
|
log.Println("ExitCode:" , ret.ExitCode )
|
||||||
|
log.Println("StdOut:" , ret.StdOut )
|
||||||
|
log.Println("StdErr:" , ret.StdErr )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Exec1SimpleTestErrParm() {
|
||||||
|
ret := Exec1Simple(
|
||||||
|
"sleep",
|
||||||
|
"v",
|
||||||
|
)
|
||||||
|
Exec1Print(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1SimpleTestErrExec() {
|
||||||
|
ret := Exec1Simple(
|
||||||
|
"data",
|
||||||
|
)
|
||||||
|
Exec1Print(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1SimpleTestShotSuccess() {
|
||||||
|
ret := Exec1Simple(
|
||||||
|
"date",
|
||||||
|
)
|
||||||
|
Exec1Print(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1SimpleTestLongSuccess() {
|
||||||
|
ret := Exec1Simple(
|
||||||
|
"sleep",
|
||||||
|
"15",
|
||||||
|
)
|
||||||
|
Exec1Print(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec1SimpleTestAll() {
|
||||||
|
log.Println("====", "Exec1SimpleTestErrParm")
|
||||||
|
|
||||||
|
Exec1SimpleTestErrParm()
|
||||||
|
|
||||||
|
log.Println("====", "Exec1SimpleTestErrExec")
|
||||||
|
|
||||||
|
Exec1SimpleTestErrExec()
|
||||||
|
|
||||||
|
log.Println("====", "Exec1SimpleTestShotSuccess")
|
||||||
|
|
||||||
|
Exec1SimpleTestShotSuccess()
|
||||||
|
|
||||||
|
log.Println("====", "Exec1SimpleTestLongSuccess")
|
||||||
|
|
||||||
|
Exec1SimpleTestLongSuccess()
|
||||||
|
}
|
Reference in New Issue
Block a user