【51CTO.com快译】有时,您需要一种快速可靠的方式向用户分发数据或软件,无需使用包管理器(比如说,最终用户可能没有安装应用程序的root访问权限)。
这个问题可以通过使用容器和Podman或Docker来解决,但如果它们在目标系统上没有,怎么办?如果要求之一是应用程序在裸机环境中正常工作,又怎么办?
您可以将Python与pip一起使用(您可能知道也可以打包非Python工件),但是那样可能面临一些安装上的限制(虚拟环境或--user选项),更不用说需要样板代码来打包Python代码了。
我在本文中演示了一种非常小巧但有效的技术来编写不需要提升权限的自解压脚本。
设置您的数据
Damiaan Zwietering有一个很酷的关于新冠病毒的Git代码存储库,带有数据和可视化(Jupyter书籍和Excel电子表格),但没有安装程序。假设您想将其提供给自己的用户,但他们无权访问Git。您就可以为用户创建自解压安装程序。
在实际情况下,您已经拥有想要分发的数据。但是您有一些示例数据要处理,首先将该存储库克隆到主目录:
- $ git clone https://gitlab.com/dzwietering/corona.git
现在有很多数据和不那么浅的目录结构,但是您可以使用git archive命令创建一个.tar文件:
- $ cd $HOME / corona
- $ git archive --verbose --output $tempdir/corona.tar.gz HEAD
对于本示例,该打包文件是您想要与用户共享的文件。
自解压脚本的结构
自解压脚本分为以下几个部分:
1. 帮助用户提取数据的代码(“有效载荷”)
2. 从脚本中分离数据(要提取)的锚点
3. 提取其后数据的锚点位置
事实证明,Bash非常擅长以这种方式来定义脚本。
创建有效载荷
这里有个想法:假设您需要分发的数据是一个含有许多脚本和数据的目录。您希望保持权限和结构完好无缺,您希望用户将其“解压缩”到主目录中。
这听起来像是tar命令的工作。但是假设您的用户不知道如何使用tar,或者他们在安装tarball文件时想要特殊选项(比如仅提取特定文件)。
另一个问题是您的.tar存档是一个二进制文件。如果您要通过电子邮件发送,必须使用Uuencode或Base64对其进行正确编码,以便安全传输。
该怎么办?不要扔掉.tar文件。相反,准备好它,以便您可以将其附加到Bash脚本(您将很快编写):
- $ base64 $tempdir/corona.tar.gz > $tempdir/corona_payload
- $ file $tempdir/corona_payload
- /tmp/tmp.8QNdzdKEkG/corona_payload: ASCII text
从.tar文件提取数据
您可以将所有内容倒到新目录中:
- $ newbase=$HOME/coronadata
- $ test ! -d $newbase && /bin/mkdir --parents --verbose $newbase
- $ tar --directory $newbase \
- --file corona.tar.gz --extract --gzip --verbose
也可以仅提取其中的一部分,比如措施、试验和测试目录。
- $ newbase=$HOME/coronadata
- $ test ! -d $newbase && /bin/mkdir --parents --verbose $newbase
- $ tar --directory $newbase --file corona.tar.gz \
- --extract --gzip --verbose measures experiment test
针对这次演练,将全部内容提取到基本目录(比如$HOME),因此结果如下:
- $HOME/$COVIDUSERDIR
剖析自解压脚本
以下是我的自解压脚本的代码。您可以将脚本保存在自己的Git存储库中,并重复用于其他部署环境。要注意的事项如下:
- SCRIPT_END是有效载荷在脚本里面开始的位置。
- 它净化用户输入。
- 一旦您搞清楚了元数据的位置,从脚本中提取($0),重新解码成二进制代码,然后拆包。
- #!/usr/bin/env bash
- # Author: Jose Vicente Nunez
- SCRIPT_END=$(/bin/grep --max-count 2 --line-number ___END_OF_SHELL_SCRIPT___ "$0"| /bin/cut --field 1 --delimiter :| /bin/tail -1)|| exit 100
- ((SCRIPT_END+=1))
- basedir=
- while test -z "$basedir"; do
- read -r -p "Where do you want to extract the COVID-19 data, relative to $HOME? (example: mydata -> $HOME/mydata. Press CTRL-C to abort):" basedir
- done
- :<<DOC
- Sanitize the user input. This is quite restrictive, so it depends of the real application requirements.
- DOC
- CLEAN=${basedir//_/}
- CLEAN=${CLEAN// /_}
- CLEAN=${CLEAN//[^a-zA-Z0-9_]/}
- if [ ! -d "$HOME/$CLEAN" ]; then
- echo "[INFO]: Will try to create the directory $HOME/$CLEAN"
- if ! /bin/mkdir --parent --verbose "$HOME/$CLEAN"; then
- echo "[ERROR]: Failed to create $HOME/$CLEAN"
- exit 100
- fi
- fi
- /bin/tail --lines +"$SCRIPT_END" "$0"| /bin/base64 -d| /bin/tar --file - --extract --gzip --directory "$HOME/$CLEAN"
- exit 0
- # Here's the end of the script followed by the embedded file
- ___END_OF_SHELL_SCRIPT___
那么您如何将有效载荷添加到脚本?只要用一点cat粘合剂将两部分组合起来:
- $ cat covid_extract.sh \
- $tempdir/corona_payload > covid_final_installer.sh
使它变成可执行:
- $ chmod u+x covid_final_installer.sh
您可以看到安装程序如何与有效载荷结合起来。由于含有有效载荷,它很庞大。
运行安装程序
这管用吗?不妨亲自试一下:
- $ ./covid_final_installer.sh
- Where do you want to extract the COVID-19 data, relative to /home/josevnz? (example: mydata -> /home/josevnz/mydata. Press CTRL-C to abort):COVIDDATA
- [INFO]: Will try to create the directory /home/josevnz/COVIDDATA
- /bin/mkdir: created directory '/home/josevnz/COVIDDATA'
- $ tree /home/josevnz/COVIDDATA
- /home/josevnz/COVIDDATA
- ├── acaps_covid19_government_measures_dataset_0.xlsx
- ├── acaps_covid19_government_measures_dataset.xlsx
- ├── COVID-19-geographic-disbtribution-worldwide.xlsx
- ├── EUCDC.ipynb
- ├── experiment
- ...
自解压安装程序很有用
我觉得自解压安装程序很有用,原因多多。
首先,您可以随意将它们做成很复杂或很简单。最复杂的部分在于规定脚本应从哪里提取有效载荷。
知道这种方法很实用,因为恶意软件安装程序也使用这种方法。现在您有了更充分的准备可以在脚本中发现这样的代码。同样重要的是,现在您知道如何通过在自己的自解压脚本中验证用户输入来阻止外壳注入滥用现象。
原文标题:Package software and data with self-compressed scripts,作者:Jose Vicente Nunez
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】