从Bash渐进式迁移到Golang命令行API开发指南
从Bash渐进式迁移到Golang命令行API开发指南
https://github.com/containers/toolbox 是一个非常实用的 Bash 脚本,它可以启动一个非特权 podman 容器,以便安全地安装库和修改系统文件,而不会影响父系统。
然而,该工具的开发已经停滞,因为项目正在进行一次用 Go 语言的完全重写 - https://github.com/containers/toolbox/pull/318
作为一名用户,我觉得这种策略令人沮丧。我希望在 Bash 工具的某些部分被重写并移植到 Go 语言的过程中,能够继续使用和修补这个 Bash 工具。有些错误只在 Go 分支中得到修复,并且由于 Go 分支使用 bash 测试来保持一致性,那里的 bash 代码也包含了修复。但它并没有发布。
想法
一种从 bash 中枚举和调用 go 函数的标准方法。
goprog apilist - 获取 goprog 中的函数列表
goprog apicheck <function> - 检查函数是否存在
goprog apihelp <function> - 获取函数参数和描述
goprog apicall <function> [param1] [param2]
然后在 bash 中检查函数列表,并将现有的 bash 函数重定向到 goprog 中的 apicall。
这可能吗?
更多关于从Bash渐进式迁移到Golang命令行API开发指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
abitrolly:
一种从Bash中枚举和调用
go函数的标准方法。
abitrolly:
这可行吗?
这本质上是在一个独立的Github仓库中编写你自己的名为goprog的Go程序。因此,在你这边是可以实现的。
至于CLI接口,你可以查看 GitHub - spf13/cobra: 现代Go CLI交互的指挥官 和 GitHub - spf13/viper: 带尖牙的Go配置管理。
祝你Go之旅愉快。
abitrolly:
作为一名用户,我发现这种策略令人沮丧。我希望在工具的某些部分被重写并移植到Go的同时,能够继续使用和修补Bash工具。有些bug只在Go分支中修复了,并且因为Go分支使用Bash测试来保持一致性,那里的Bash代码也包含了修复。但它没有被发布。
问题的核心在于工具箱设计的范围和方向。你可能需要将这个问题反馈回那个Github工具箱的问题工单。
如果Go程序被正确实现,你就不应该在其开发和生产操作(包括单元测试)中使用shell脚本包装器。
更多关于从Bash渐进式迁移到Golang命令行API开发指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
从Bash渐进式迁移到Go语言命令行API开发是可行的技术方案。以下是实现你描述的标准API调用方法的Go代码示例:
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
)
// API函数注册表
var apiFunctions = map[string]func([]string) error{
"listContainers": listContainers,
"createContainer": createContainer,
"removeContainer": removeContainer,
"executeCommand": executeCommand,
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: goprog <command> [args...]")
os.Exit(1)
}
command := os.Args[1]
args := os.Args[2:]
switch command {
case "apilist":
apiList()
case "apicheck":
if len(args) < 1 {
fmt.Println("Usage: goprog apicheck <function>")
os.Exit(1)
}
apiCheck(args[0])
case "apihelp":
if len(args) < 1 {
fmt.Println("Usage: goprog apihelp <function>")
os.Exit(1)
}
apiHelp(args[0])
case "apicall":
if len(args) < 1 {
fmt.Println("Usage: goprog apicall <function> [params...]")
os.Exit(1)
}
apiCall(args[0], args[1:])
default:
fmt.Printf("Unknown command: %s\n", command)
os.Exit(1)
}
}
func apiList() {
funcList := make([]string, 0, len(apiFunctions))
for name := range apiFunctions {
funcList = append(funcList, name)
}
output, _ := json.Marshal(funcList)
fmt.Println(string(output))
}
func apiCheck(function string) {
_, exists := apiFunctions[function]
fmt.Println(exists)
}
func apiHelp(function string) {
helpTexts := map[string]string{
"listContainers": "listContainers [filter] - 列出容器,可选过滤条件",
"createContainer": "createContainer <name> <image> - 创建新容器",
"removeContainer": "removeContainer <name> [force] - 删除容器",
"executeCommand": "executeCommand <container> <command> - 在容器内执行命令",
}
if help, exists := helpTexts[function]; exists {
fmt.Println(help)
} else {
fmt.Printf("No help available for function: %s\n", function)
}
}
func apiCall(function string, params []string) {
if fn, exists := apiFunctions[function]; exists {
err := fn(params)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
} else {
fmt.Printf("Function not found: %s\n", function)
os.Exit(1)
}
}
// 实际的API函数实现
func listContainers(args []string) error {
filter := ""
if len(args) > 0 {
filter = args[0]
}
// 这里实现实际的容器列出逻辑
fmt.Printf("Listing containers with filter: %s\n", filter)
return nil
}
func createContainer(args []string) error {
if len(args) < 2 {
return fmt.Errorf("createContainer requires name and image parameters")
}
name := args[0]
image := args[1]
// 这里实现实际的容器创建逻辑
fmt.Printf("Creating container %s from image %s\n", name, image)
return nil
}
func removeContainer(args []string) error {
if len(args) < 1 {
return fmt.Errorf("removeContainer requires container name")
}
name := args[0]
force := false
if len(args) > 1 && strings.ToLower(args[1]) == "true" {
force = true
}
// 这里实现实际的容器删除逻辑
fmt.Printf("Removing container %s (force: %v)\n", name, force)
return nil
}
func executeCommand(args []string) error {
if len(args) < 2 {
return fmt.Errorf("executeCommand requires container name and command")
}
container := args[0]
command := args[1]
// 这里实现实际的命令执行逻辑
fmt.Printf("Executing command '%s' in container %s\n", command, container)
return nil
}
对应的Bash脚本示例:
#!/bin/bash
# 检查Go函数是否存在
if goprog apicheck "listContainers" | grep -q "true"; then
# 使用Go版本
goprog apicall "listContainers" "$@"
else
# 回退到Bash版本
bash_list_containers "$@"
fi
# 批量迁移检查
MIGRATE_FUNCTIONS=("listContainers" "createContainer" "removeContainer" "executeCommand")
for func in "${MIGRATE_FUNCTIONS[@]}"; do
if goprog apicheck "$func" | grep -q "true"; then
# 创建包装函数
eval "
${func}() {
goprog apicall \"${func}\" \"\$@\"
}
"
echo "Migrated $func to Go version"
fi
done
# 动态获取所有可用函数
AVAILABLE_FUNCTIONS=$(goprog apilist | tr -d '[]"' | tr ',' '\n')
echo "Available Go API functions:"
echo "$AVAILABLE_FUNCTIONS"
构建和测试命令:
# 构建Go程序
go build -o goprog main.go
# 测试API列表
./goprog apilist
# 输出: ["listContainers","createContainer","removeContainer","executeCommand"]
# 测试函数检查
./goprog apicheck listContainers
# 输出: true
# 测试帮助信息
./goprog apihelp createContainer
# 输出: createContainer <name> <image> - 创建新容器
# 测试API调用
./goprog apicall createContainer mycontainer fedora:latest
# 输出: Creating container mycontainer from image fedora:latest
这个方案允许你在Bash脚本中逐步替换函数调用,同时保持向后兼容性。每个Bash函数可以先检查Go版本是否存在,如果存在则调用Go版本,否则继续使用Bash实现。

