Skip to content

Commit

Permalink
增加别名和增加流式处理函数
Browse files Browse the repository at this point in the history
  • Loading branch information
yangyile committed Dec 16, 2024
1 parent 5f4b126 commit cb31b8d
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 68 deletions.
110 changes: 88 additions & 22 deletions command.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package osexec

import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"
"sync"

"github.com/yyle88/erero"
"github.com/yyle88/eroticgo"
"github.com/yyle88/osexec/internal/utils"
"github.com/yyle88/printgo"
"github.com/yyle88/tern"
"github.com/yyle88/zaplog"
Expand Down Expand Up @@ -95,27 +100,40 @@ func (c *CommandConfig) WithDebugMode(debugMode bool) *CommandConfig {
// Exec executes a shell command with the specified name and arguments, using the CommandConfig configuration.
// Exec 使用 CommandConfig 的配置执行带有指定名称和参数的 shell 命令。
func (c *CommandConfig) Exec(name string, args ...string) ([]byte, error) {
if err := c.validateConfig(name, args); err != nil {
return nil, erero.Ero(err)
}
command := c.prepareCommand(name, args)
output, err := command.CombinedOutput()
return utils.WarpMessage(output, err, c.DebugMode)
}

func (c *CommandConfig) validateConfig(name string, args []string) error {
if name == "" {
return nil, erero.New("can-not-execute-with-empty-command-name")
return erero.New("can-not-execute-with-empty-command-name")
}
if strings.Contains(name, " ") {
return nil, erero.New("can-not-contains-space-in-command-name")
return erero.New("can-not-contains-space-in-command-name")
}
if c.ShellFlag != "" {
if c.ShellType == "" {
return nil, erero.New("can-not-execute-with-wrong-shell-command")
return erero.New("can-not-execute-with-wrong-shell-command")
}
}
if c.ShellType != "" {
if c.ShellFlag != "-c" {
return nil, erero.New("can-not-execute-with-wrong-shell-options")
return erero.New("can-not-execute-with-wrong-shell-options")
}
}
if c.DebugMode {
debugMessage := c.makeCommandMessage(name, args)
showMessage(debugMessage)
utils.ShowCommand(debugMessage)
zaplog.ZAPS.P1.LOG.Debug("EXEC:", zap.String("CMD", debugMessage))
}
return nil
}

func (c *CommandConfig) prepareCommand(name string, args []string) *exec.Cmd {
cmd := tern.BFF(c.ShellType != "",
func() *exec.Cmd {
return exec.Command(c.ShellType, c.ShellFlag, name+" "+strings.Join(args, " "))
Expand All @@ -127,23 +145,7 @@ func (c *CommandConfig) Exec(name string, args ...string) ([]byte, error) {
cmd.Env = tern.BF(len(c.Envs) > 0, func() []string {
return append(os.Environ(), c.Envs...)
})
return c.warpCommandOutput(cmd.CombinedOutput())
}

// warpCommandOutput processes the output and error from the command execution, adding debug information if necessary.
// warpCommandOutput 处理命令执行的输出和错误,并在需要时添加调试信息。
func (c *CommandConfig) warpCommandOutput(output []byte, erx error) ([]byte, error) {
if erx != nil {
if c.DebugMode {
if len(output) > 0 {
showWarning(string(output))
} else {
showWarning(erx.Error())
}
}
return output, erero.Wro(erx)
}
return output, nil
return cmd
}

// makeCommandMessage constructs a command-line string based on the CommandConfig and given command name and arguments.
Expand All @@ -163,3 +165,67 @@ func (c *CommandConfig) makeCommandMessage(name string, args []string) string {
}
return pts.String()
}

func (c *CommandConfig) StreamExec(name string, args ...string) ([]byte, error) {
if err := c.validateConfig(name, args); err != nil {
return nil, erero.Ero(err)
}
command := c.prepareCommand(name, args)

stdoutPipe, err := command.StdoutPipe()
if err != nil {
return nil, erero.Wro(err)
}

stderrPipe, err := command.StderrPipe()
if err != nil {
return nil, erero.Wro(err)
}

stdoutReader := bufio.NewReader(stdoutPipe)
stderrReader := bufio.NewReader(stderrPipe)
if err := command.Start(); err != nil {
return nil, erero.Wro(err)
}

wg := sync.WaitGroup{}
wg.Add(2)
stderrBuffer := printgo.NewPTX()
go func() {
defer wg.Done()
c.readPipe(stderrReader, stderrBuffer, "REASON", eroticgo.RED)
}()
stdoutBuffer := printgo.NewPTX()
go func() {
defer wg.Done()
c.readPipe(stdoutReader, stdoutBuffer, "OUTPUT", eroticgo.GREEN)
}()
wg.Wait()

if stderrBuffer.Len() > 0 {
return utils.WarpMessage(stdoutBuffer.Bytes(), erero.New(stderrBuffer.String()), c.DebugMode)
} else {
return utils.WarpMessage(stdoutBuffer.Bytes(), nil, c.DebugMode)
}
}

func (c *CommandConfig) readPipe(reader *bufio.Reader, ptx *printgo.PTX, debugMessage string, erotic eroticgo.COLOR) {
for {
streamLine, _, err := reader.ReadLine()

if c.DebugMode {
zaplog.SUG.Debugln(debugMessage, erotic.Sprint(string(streamLine)))
}

if err != nil {
if err == io.EOF {
ptx.Write(streamLine)
return
}
panic(erero.Wro(err)) //panic: 读取结果出错很罕见
} else {
ptx.Write(streamLine)
ptx.Println()
}
}
}
47 changes: 47 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package utils

import (
"fmt"

"github.com/yyle88/erero"
"github.com/yyle88/eroticgo"
)

func ShowCommand(message string) {
fmt.Println(eroticgo.BLUE.Sprint("---"))
fmt.Println(eroticgo.BLUE.Sprint(message))
fmt.Println(eroticgo.BLUE.Sprint("---"))
}

func ShowMessage(message string) {
fmt.Println(eroticgo.GREEN.Sprint("---"))
fmt.Println(eroticgo.GREEN.Sprint(message))
fmt.Println(eroticgo.GREEN.Sprint("---"))
}

func ShowWarning(message string) {
fmt.Println(eroticgo.RED.Sprint("---"))
fmt.Println(eroticgo.RED.Sprint(message))
fmt.Println(eroticgo.RED.Sprint("---"))
}

// WarpMessage handles the output of the executed command and wraps errors.
// WarpMessage 处理执行命令的输出,并在出现错误时封装错误信息。
func WarpMessage(output []byte, err error, debugMode bool) ([]byte, error) {
if err != nil {
if debugMode {
if len(output) > 0 {
ShowWarning(string(output))
} else {
ShowWarning(err.Error())
}
}
return output, erero.Wro(err)
}
if debugMode {
if len(output) > 0 {
ShowMessage(string(output))
}
}
return output, nil
}
37 changes: 13 additions & 24 deletions osexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/yyle88/erero"
"github.com/yyle88/osexec/internal/utils"
"github.com/yyle88/zaplog"
"go.uber.org/zap"
)
Expand All @@ -22,11 +23,12 @@ func Exec(name string, args ...string) ([]byte, error) {
}
if debugModeOpen {
debugMessage := strings.TrimSpace(fmt.Sprintf("%s %s", name, strings.Join(args, " ")))
showMessage(debugMessage)
utils.ShowCommand(debugMessage)
zaplog.ZAPS.P1.LOG.Debug("EXEC:", zap.String("CMD", debugMessage))
}
command := exec.Command(name, args...)
return warpCommandOutput(command.CombinedOutput())
output, err := command.CombinedOutput()
return utils.WarpMessage(output, err, debugModeOpen)
}

// ExecInPath executes a command in a specified directory.
Expand All @@ -43,12 +45,13 @@ func ExecInPath(path string, name string, args ...string) ([]byte, error) {
}
if debugModeOpen {
debugMessage := strings.TrimSpace(fmt.Sprintf("cd %s && %s", path, makeCommandMessage(name, args)))
showMessage(debugMessage)
utils.ShowCommand(debugMessage)
zaplog.ZAPS.P1.LOG.Debug("EXEC_IN_PATH:", zap.String("CMD", debugMessage))
}
command := exec.Command(name, args...)
command.Dir = path
return warpCommandOutput(command.CombinedOutput())
output, err := command.CombinedOutput()
return utils.WarpMessage(output, err, debugModeOpen)
}

// ExecInEnvs executes a command with custom environment variables.
Expand All @@ -62,13 +65,14 @@ func ExecInEnvs(envs []string, name string, args ...string) ([]byte, error) {
}
if debugModeOpen {
debugMessage := strings.TrimSpace(fmt.Sprintf("%s %s", strings.Join(envs, " "), makeCommandMessage(name, args)))
showMessage(debugMessage)
utils.ShowCommand(debugMessage)
zaplog.ZAPS.P1.LOG.Debug("EXEC_IN_ENVS:", zap.String("CMD", debugMessage))
}
command := exec.Command(name, args...)
command.Env = os.Environ() // Add custom environment variables
command.Env = append(command.Env, envs...)
return warpCommandOutput(command.CombinedOutput())
output, err := command.CombinedOutput()
return utils.WarpMessage(output, err, debugModeOpen)
}

// ExecXshRun executes a command using a specific shell type and shell flag.
Expand All @@ -88,27 +92,12 @@ func ExecXshRun(shellType, shellFlag string, name string, args ...string) ([]byt
}
if debugModeOpen {
debugMessage := strings.TrimSpace(fmt.Sprintf("%s %s '%s'", shellType, shellFlag, escapeSingleQuotes(makeCommandMessage(name, args))))
showMessage(debugMessage)
utils.ShowCommand(debugMessage)
zaplog.ZAPS.P1.LOG.Debug("EXEC_XSH_RUN:", zap.String("CMD", debugMessage))
}
command := exec.Command(shellType, "-c", name+" "+strings.Join(args, " "))
return warpCommandOutput(command.CombinedOutput())
}

// warpCommandOutput handles the output of the executed command and wraps errors if they occur.
// warpCommandOutput 处理执行命令的输出,并在出现错误时封装错误信息。
func warpCommandOutput(output []byte, erx error) ([]byte, error) {
if erx != nil {
if debugModeOpen {
if len(output) > 0 {
showWarning(string(output))
} else {
showWarning(erx.Error())
}
}
return output, erero.Wro(erx)
}
return output, nil
output, err := command.CombinedOutput()
return utils.WarpMessage(output, err, debugModeOpen)
}

// makeCommandMessage formats a command name and its arguments into a single command-line string.
Expand Down
24 changes: 6 additions & 18 deletions utils.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
package osexec

import (
"fmt"

"github.com/yyle88/eroticgo"
)

type CMC = CommandConfig

func NewCMC() *CMC {
return NewCommandConfig()
}

var debugModeOpen = true
type OsCommand = CommandConfig

func SetDebugMode(enable bool) {
debugModeOpen = enable
func NewOsCommand() *OsCommand {
return NewCommandConfig()
}

func showMessage(message string) {
fmt.Println(eroticgo.BLUE.Sprint("---"))
fmt.Println(eroticgo.BLUE.Sprint(message))
fmt.Println(eroticgo.BLUE.Sprint("---"))
}
var debugModeOpen = true

func showWarning(message string) {
fmt.Println(eroticgo.RED.Sprint("---"))
fmt.Println(eroticgo.RED.Sprint(message))
fmt.Println(eroticgo.RED.Sprint("---"))
func SetDebugMode(enable bool) {
debugModeOpen = enable
}
22 changes: 18 additions & 4 deletions utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"github.com/yyle88/runpath"
)

func TestSetDebugMode(t *testing.T) {
Expand All @@ -12,10 +13,23 @@ func TestSetDebugMode(t *testing.T) {
}

func TestNewCMC(t *testing.T) {
commandConfig := NewCMC()
commandConfig.WithShell("bash", "-c")
commandConfig.WithEnvs([]string{"A=1", "B=2"})
data, err := commandConfig.Exec("echo", "$A", "$B")
cmc := NewCMC()
cmc.WithShell("bash", "-c")
cmc.WithEnvs([]string{"A=1", "B=2"})
data, err := cmc.Exec("echo", "$A", "$B")
require.NoError(t, err)
t.Log(string(data))
}

func TestNewOsCommand(t *testing.T) {
root := runpath.PARENT.Path()
t.Log(root)

osCommand := NewOsCommand()
osCommand.WithPath(root)
osCommand.WithShell("bash", "-c")
osCommand.WithEnvs([]string{"A=1", "B=2"})
data, err := osCommand.StreamExec("echo", "$A", "$B")
require.NoError(t, err)
t.Log(string(data))
}

0 comments on commit cb31b8d

Please sign in to comment.