go 1.16+ 支持 `embed` 包,可将 bash 脚本以字符串形式编译进二进制;配合 `exec.command("bash")` 并设置 `stdin`,即可直接执行,无需外部文件依赖,完美支持交叉编译。
在 Go 应用中动态执行 Shell 脚本是常见需求(如初始化环境、调试辅助、CI 工具封装等),但传统方式需分发脚本文件,破坏单一二进制优势,且易因路径或权限问题失败。Go 1.16 引入的 embed 包为此提供了优雅解法:将脚本内容静态嵌入编译后的二进制中,并通过标准输入(stdin)交由 bash 或 sh 解释执行。
只需两步:
package main
import (
"embed"
"fmt"
"os/exec"
"strings"
)
//go:embed script.sh
var script string // 类型为 string,自动读取文件 UTF-8 内容
func main() {
cmd := exec.Command("bash")
cmd.Stdin = strings.NewReader(script)
output, err := cmd.Output()
if err != nil {
fmt.Printf("执行失败: %v\n", err)
return
}
fmt.Println(string(output))
}⚠️ 注意事项:
Bash 支持 -s 标志从 stdin 读取脚本,并将后续参数作为 , , … $@ 传入:
package main
import (
"fmt"
"os/exec"
"strings"
)
func main() {
// -s: 从 stdin 读脚本;-: 占位符(表示脚本结束位置);后续为 $1, $2...
cmd := exec.Command("bash", "-s", "-", "hello", "world")
cmd.Stdin = strings.NewReader(`
echo "参数个数: $#"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "全部参数: $@"
`)
out, err := cmd.Output()
if err != nil {
fmt.Printf("执行出错: %v\n", err)
return
}
fmt.Print(string(out))
}输出示例:
参数个数: 2 第一个参数: hello第二个参数: world 全部参数: hello world
通过 embed + exec 组合,你既能享受 Go 单体二进制的部署便利,又能复用成熟的 Shell 生态——无需妥协,开箱即用。