起因是公司需求,要求我们的部署程序最终可以构建出一个文件,上传全新的纯内网服务器后,自动化部署。
而这个服务器是连 unzip 或 tar 都没有的环境,无奈只能通过 sh 脚本释放 tar 来解压自带的安装包了。
核心代码为如下两行:
tail --lines=+${attachment_start_line} "$0" > ./_tmp
为实现将 sh 脚本若干行后的内容提取到文件
head --bytes=${file_size} ./_tmp > ${file_name}
为实现将 文件的前若干字节提取到文件。
所以思路上就很简单了,这种所谓的自释放、自解压,其实网上已经有很多同类的脚本的,但普遍都只能释放一个文件,而我的这个需求,最少需要带两个文件,一个是 tar 的程序,一个是系统的安装包。
所以,自己就完善了一下功能,写出这套脚本,功能比较完善了,具有一下功能:
- 打包脚本(可以将制定目录下的所有文件都打包在一起)
- 解压后的 md5 校验,可以保证释放的文件是一致的
- 可以自定义添加释放后执行的脚本
具体代码可移步 github.gist
打包脚本:build-pack.sh
#!/bin/bash
# 如果没有 传递文件夹参数
source_dir=$1
if [ -z "${source_dir}" ]; then
echo "请输入源文件夹路径"
echo "Usage: $0 source_dir"
exit 1
fi
# 需要压缩的文件列表
files=($(ls $source_dir))
# 自释放模板文件
unpack_tpl=./unpack-tpl.sh
# 生成结果
unpack_done=./unpack.sh
# 最终的临时文件
cp "${unpack_tpl}" "${unpack_done}"
# 获取标记行位置
attachment_start_line=($(awk '/^__ARCHIVE_BELOW__/ {; print NR; exit 0;}' "${unpack_done}"))
echo "Attachment Start Line: ${attachment_start_line}"
# 遍历文件获取文件尺寸头信息
file_info=()
for file in ${files[@]}; do
echo -e "File Name: $file,\c"
# 获取文件尺寸
file_size=$(stat -c%s "${source_dir}/$file")
echo -e " File Size: $file_size, File Md5: \c"
# 获取文件 md5
file_md5=$(md5sum "${source_dir}/${file}" | awk '{print $1}')
echo -e "$file_md5\c"
# 记录文件信息
file_info[${#file_info[@]}]="${file},${file_size},${file_md5}"
# 将文件追加到文件后面
echo -e " Packing...\c"
cat "${source_dir}/${file}" >>"${unpack_done}"
echo " Done"
done
# 替换文件列表
echo -e "Save File Info To File...\c"
file_info=$(
IFS=:
echo "${file_info[*]}"
)
sed -i "${attachment_start_line}s/^__ARCHIVE_BELOW__/__ARCHIVE_BELOW__[${file_info}]/" "${unpack_done}"
echo -e "\nBuild Pack Done, File Save To ${unpack_done}"
# 查看结果头信息
# awk '/^__ARCHIVE_BELOW__/ {; print $1; exit 0;}' ./untardone.sh
解包脚本模板:unpack-tpl.sh
#!/bin/bash
# 获取解压目录
output_dir=$1
if [ "${output_dir}" == "" ]; then
output_dir=$(mktemp -d)
fi
echo "Output Dir: ${output_dir}"
# 取出附件信息
attachment_info=($(awk '/^__ARCHIVE_BELOW__/ {files=gensub(/.*\[([^]]+)]/,"\\1",1,$0); print NR+1,files;exit 0;}' $0))
# 取出附件开始位置
attachment_start_line=${attachment_info[0]}
echo "Attachment Start Line: ${attachment_start_line}"
# 临时的过度文件
temp_file=$(mktemp)
echo "Unpack Temp File: ${temp_file}"
# 释放文件
tail -n+$attachment_start_line "$0" >${temp_file}
# 取出附件文件列表
attachment_files=(${attachment_info[1]//:/ })
# echo "Attachment Files: ${attachment_files[@]}"
# 获取一个文件信息
getFile() {
# 获取文件信息
file_info=$1
# 拆分结构
file_info=(${file_info//,/ })
# 文件名
file_name="${file_info[0]}"
file_path="${output_dir}/${file_info[0]}"
# 文件尺寸
file_size=${file_info[1]}
# 文件 hash
file_md5=${file_info[2]}
# 调试输出
echo "File Name: ${file_name}, Save Path: ${file_path}, File Size: ${file_size}, File Md5: ${file_md5}"
}
# 以字节为单位读取文件
for file_info in ${attachment_files[@]}; do
# 获取文件信息
getFile $file_info
# 读取文件头一段长度到文件
echo "Read File To ${file_path} ..."
head --bytes=${file_size} ${temp_file} >${file_path}
# 重新计算 文件 md5
echo -e "Calculate File Md5 \c"
file_md5=$(md5sum ${file_path} | awk '{print $1}')
if [ "${file_md5}" != "${file_md5}" ]; then
echo "${file_path} Error"
else
echo "${file_md5} Success"
fi
# 重置临时文件
echo "Reset Temp File ..."
file_size=$(($file_size + 1))
tail --bytes=+${file_size} ${temp_file} >"${temp_file}2"
rm -rf ${temp_file}
mv "${temp_file}2" ${temp_file}
done
# 删除临时文件
rm -rf ${temp_file}
###### 用户的自定义代码 开始 ######
#
# 文件释放后,并没有解压功能,需要自己打包时带上解压程序,并自己在下面调用程序解压
# After the file is released, there is no decompression function.
# You need to take the decompression program with you when you pack it,
# and call the program below to extract it.
#
# 以下为示例代码 The following is the sample code
# 进入临时文件夹
cd $output_dir
# 安装 tar
rpm -ivh --nodeps --force tar-1.30-5.an8.x86_64.rpm
# 解压出文件
echo "Untar ..."
tar -xvf *.tar.gz
# 执行安装脚本
echo "Sleep 10s, Execute Install Script ..."
sleep 10s
chmod +x *.sh
./install.sh
###### 用户的自定义代码 结束 以下勿动 ######
exit 0
__ARCHIVE_BELOW__