golang实现Unix Shell风格命令链式执行的插件库command-chain的使用

golang实现Unix Shell风格命令链式执行的插件库command-chain的使用

go-command-chain是一个用于轻松配置和运行命令链的Go库,类似于Unix shell中的管道操作。

基础示例

链式命令执行

以下是一个模拟Unix Shell管道操作的示例,统计日志文件中包含"error"的行数:

package main

import (
	"fmt"
	"github.com/rainu/go-command-chain"
)

func main() {
	stdOut, stdErr, err := cmdchain.Builder().
		Join("cat", "log_file.txt").    // 第一个命令:读取文件
		Join("grep", "error").         // 第二个命令:过滤包含error的行
		Join("wc", "-l").              // 第三个命令:统计行数
		Finalize().RunAndGet()         // 执行并获取结果

	if err != nil {
		panic(err)
	}
	if stdErr != "" {
		panic(stdErr)
	}
	fmt.Printf("Errors found: %s", stdOut)
}

直接执行Shell命令字符串

你也可以直接执行完整的Shell命令字符串:

package main

import (
	"fmt"
	"github.com/rainu/go-command-chain"
)

func main() {
	stdOut, stdErr, err := cmdchain.Builder().
		JoinShellCmd(`cat log_file.txt | grep error | wc -l`).  // 直接执行Shell管道命令
		Finalize().RunAndGet()

	if err != nil {
		panic(err)
	}
	if stdErr != "" {
		panic(stdErr)
	}
	fmt.Printf("Errors found: %s", stdOut)
}

高级功能

输入注入

可以为每个命令注入多个不同的输入流:

package main

import (
	"github.com/rainu/go-command-chain"
	"strings"
)

func main() {
	inputContent1 := strings.NewReader("content from application itself\n")
	inputContent2 := strings.NewReader("another content from application itself\n")

	err := cmdchain.Builder().
		Join("echo", "test").WithInjections(inputContent1, inputContent2).  // 注入两个输入流
		Join("grep", "test").
		Join("wc", "-l").
		Finalize().Run()

	if err != nil {
		panic(err)
	}
}

标准输出和错误输出的分流

可以为每个命令的标准输出和错误输出指定不同的写入目标:

package main

import (
	"bytes"
	"github.com/rainu/go-command-chain"
)

func main() {
	echoErr := &bytes.Buffer{}  // 存储echo命令的错误输出
	echoOut := &bytes.Buffer{}  // 存储echo命令的标准输出
	grepErr := &bytes.Buffer{}  // 存储grep命令的错误输出
	
	err := cmdchain.Builder().
		Join("echo", "test").WithOutputForks(echoOut).WithErrorForks(echoErr).  // 分流echo的输出
		Join("grep", "test").WithErrorForks(grepErr).                           // 分流grep的错误输出
		Join("wc", "-l").
		Finalize().Run()

	if err != nil {
		panic(err)
	}
}

为什么使用这个库?

相比直接调用shell执行管道命令,这个库有以下优势:

  1. 不依赖于系统shell及其特定版本
  2. 可以精确控制每个单独的命令
  3. 避免了复杂的管道语法问题
  4. 提供了更灵活的输入输出控制能力

相比之下,直接调用shell的方式:

package main

import (
	"os/exec"
)

func main() {
	exec.Command("sh", "-c", "cat log_file.txt | grep error | wc -l").Run()
}

存在以下问题:

  • 必须安装特定版本的shell
  • 只能控制父进程(shell命令本身)
  • 管道语法可能因shell版本而异

更多关于golang实现Unix Shell风格命令链式执行的插件库command-chain的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Unix Shell风格命令链式执行的插件库command-chain的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang实现Unix Shell风格命令链式执行 - command-chain库

command-chain是一个用于Golang的轻量级库,它允许你以类似Unix Shell管道的方式链式执行多个命令。下面我将介绍如何使用这个库。

安装

go get github.com/rainu/go-command-chain

基本用法

简单命令链

package main

import (
	"fmt"
	"os"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	// 创建命令链
	chain := commandchain.Builder().
		Join("echo", "hello world").  // 第一个命令
		Join("grep", "hello").         // 第二个命令
		Join("wc", "-l").              // 第三个命令
		Finalize()

	// 执行命令链
	err := chain.Run()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
}

带错误处理和输出捕获

package main

import (
	"fmt"
	"os"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	var output string
	
	// 创建命令链并捕获输出
	chain := commandchain.Builder().
		Join("ls", "-la").             // 列出目录内容
		Join("grep", "go.mod").        // 过滤包含go.mod的行
		Capture(&output).              // 捕获最终输出
		Finalize()

	// 执行命令链
	err := chain.Run()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Found go.mod: %s\n", output)
}

设置工作目录和环境变量

package main

import (
	"fmt"
	"os"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	var output string
	
	// 创建命令链并设置工作目录和环境变量
	chain := commandchain.Builder().
		WorkingDir("/tmp").                   // 设置工作目录
		Env("MY_VAR", "some_value").          // 设置环境变量
		Join("env").                          // 打印环境变量
		Join("grep", "MY_VAR").               // 过滤MY_VAR
		Capture(&output).                     // 捕获输出
		Finalize()

	err := chain.Run()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Environment variable: %s\n", output)
}

错误处理和调试

package main

import (
	"fmt"
	"os"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	// 创建命令链并启用调试
	chain := commandchain.Builder().
		Debug(true).                         // 启用调试输出
		Join("ls", "nonexistent_dir").       // 这个命令会失败
		Join("wc", "-l").                    // 这个命令不会执行
		Finalize()

	err := chain.Run()
	if err != nil {
		// 检查是否是退出错误
		if exitErr, ok := err.(*exec.ExitError); ok {
			fmt.Printf("Command failed with exit code %d\n", exitErr.ExitCode())
		} else {
			fmt.Printf("Error: %v\n", err)
		}
		os.Exit(1)
	}
}

高级特性

自定义命令执行器

package main

import (
	"fmt"
	"os"
	"os/exec"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	// 自定义执行器函数
	executor := func(cmd *exec.Cmd) error {
		fmt.Printf("Executing: %v\n", cmd.Args)
		return cmd.Run()
	}

	chain := commandchain.Builder().
		WithExecutor(executor).      // 使用自定义执行器
		Join("echo", "custom exec").
		Finalize()

	err := chain.Run()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
}

条件执行

package main

import (
	"fmt"
	"os"
	
	"github.com/rainu/go-command-chain/pkg"
)

func main() {
	shouldRun := true
	
	chain := commandchain.Builder().
		Join("echo", "always runs").
		JoinIf(shouldRun, "echo", "conditional command").
		Finalize()

	err := chain.Run()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
}

最佳实践

  1. 错误处理:始终检查Run()方法的返回值
  2. 资源清理:长时间运行的命令链应设置适当的超时
  3. 安全性:避免将用户输入直接传递给命令,防止命令注入
  4. 调试:在开发时启用Debug(true)以查看执行的命令

command-chain库提供了一种简洁的方式来构建和执行命令链,特别适合需要将多个命令行工具串联使用的场景。它的API设计模仿了Unix Shell的管道操作,使得代码更易于理解和维护。

回到顶部