简介
你是否曾想过用一个二进制文件在多个平台上运行Go程序?我一直希望Go能有 "胖 "二进制文件,就像我们从PowerPC转换到Intel时的老式Mac一样,让你在任何平台上运行。
那么,如果我们想发布我们的Go程序,让它从一个可执行文件中支持多种平台/操作系统的组合,我们今天能做什么呢?
可靠的老式shell脚本。
广告
如果你对使用Go进行DevOps工作感兴趣,请在亚马逊上查看我的书。Go for DevOps。
作者(不是出版商)的收益将捐给无国界医生组织,这是一个伟大的组织。
让我们把话说清楚
实际上,我讨厌大多数使用情况下的shell脚本。它们很容易出错,而且测试也很麻烦。我对shell脚本的主要使用情况是围绕一个不稳定的测试运行一个for循环,试图让它再次跳闸。
但像任何东西一样,也有例外。sh在每个Unix平台上都有,因此它的通用性使它非常适合这种用途。
是什么导致我写了这篇文章?
我最近遇到一个人试图用shell来推送数据给一个有多种语言SDK的服务。但是那个人不想为他们要支持的所有系统准备不同的二进制文件,也不想安装其他的运行系统(Python、.Net...)。这意味着在该平台的REST流上做一些逆向工程,在调用cURL时在shell中做一些复杂的体操*。*
我想一定有更好的方法,于是我做了这个演示。这个想法来自于Google和Facebook用来打包Python程序进行分发的方法。
这个想法
我们将在一个shell脚本中打包多个Go二进制文件,shell脚本将检测操作系统/平台,将我们想要的二进制文件解码到正确的位置并调用它。我们可以把shell脚本的参数直接传递给二进制文件。
现在,如果你有足够的二进制文件或大型程序,这将使shell脚本变得很大。另一个版本可以让脚本从GitHub或其他地方下载正确的版本以节省空间。但在这个演示中,我们将直接把二进制文件放进去。
这个演示的所有代码都可以在这里找到:https://github.com/johnsiilver/shellembed
包裹脚本
为了使这一切顺利进行,我们需要一个脚本来包装我们的Go二进制文件,然后在调用时解除包装。
#!/bin/bash # define the tasks that need to be done with the extracted contentprocess_tar() { cd $WORK_DIR"/bin" # do something with the extracted content eval "./hello_${os}_${arc} $@"}
首先,我们要定义一个稍后要调用的函数,叫做process_tar()。该函数将进入我们稍后创建的工作目录,并在工作目录中的二进制文件上运行eval命令,将脚本传给它的参数($@)。
# line number where payload startsPAYLOAD_LINE=$(awk '/^__PAYLOAD_BEGINS__/ { print NR + 1; exit 0; }' $0)
接下来我们用awk来扫描我们所处的脚本文件,并记录有*__PAYLOAD_BEGINS__* 的行号*。* 在这之后的一切都将是我们的Go二进制文件,这些文件以压缩的tar格式放在shell脚本的最后。
# Determine the os and arch.if [[ "$OSTYPE" == "linux-gnu"* ]]; then os="linux"elif [[ "$OSTYPE" == "darwin"* ]]; then os="darwin"else echo "unsupported OS: " + `uname -a` exit 1fi
我们想支持Linux和Darwin都运行在x86_64和arm64处理器上。我们使用*$OSTYPE和uname*来确定我们的操作系统和平台。如果有任何其他的组合出现,我们就会给出一个错误。
# make our directory if it doesn't exist.mkdir -p /tmp/helloshellWORK_DIR=/tmp/helloshell
然后我们建立一些工作目录,在这些目录中转储我们提取的tar文件,以获得Go的可执行文件。
# extract the embedded tar filetail -n +${PAYLOAD_LINE} $0 | tar -zpx -C $WORK_DIR >/dev/null 2>&1
然后,我们建立一些工作目录,将提取的 tar 文件转储到这些目录中,以获取 Go 可执行文件。
然后我们解开Go的可执行文件,执行上面的*process_tar()*函数。
现在我们有了一个脚本,如果我们支持这个平台,它可以解开一些Go二进制文件并运行它们。但是我们如何把Go二进制文件放到脚本中呢?
为平台构建脚本
这里有一个小脚本,可以为我们想要的每一个操作系统/平台组合构建我们的二进制文件,然后将它们嵌入一个名为run.sh 的文件中**。** 这个文件是hello.sh的副本,其中嵌入了Go的二进制文件作为压缩的tar文件。
这个构建器脚本将被称为build_for_platform.sh
#!/bin/bash
第一部分只是在一个名为*bin/*的目录中构建Go二进制文件,并按操作系统和平台对其进行索引。
tar -czvf hello.tar.gz bin/
接下来的部分是创建bin/的压缩包。
cp hello.sh run.shcat hello.tar.gz >> run.sh
现在我们将hello.sh的副本创建为run.sh,并将我们的tarball追加到run.sh的结尾。
rm hello.tar.gzrm -rf bin/
最后,我们做一些清理工作。
击鼓,如果你愿意的话
现在你可以简单地运行。
./build_for_platform.sh
这将创建你的run.sh文件。
现在让我们执行run.sh,并给它一个参数(这是一个它要打印出来的名字)。
./run.sh John
这将输出类似这样的东西(输出取决于平台)。
hello John from: darwin/arm64
结论
正如你从上面看到的,可以使用shell脚本在所有支持的平台上拥有一个通用的Go二进制。如果你希望保持shell脚本的规模,你可以让它简单地下载你需要的二进制文件,然后用相同的参数调用。
但是当你不想依赖互联网的时候,这是一个好办法。