您的位置:首页 > 运维架构 > Shell

Shell 自解压安装脚步

2017-10-13 11:14 176 查看
经常在安装程序的时候直接通过一个 shell 脚步就完成了,个人觉得这种方式也是挺方便的。于是就研究了一下,发现其实实现起来也并不复杂,本文主要记录一下整个制作过程。

我目前了解到的 shell 自解压的安装脚步制作方式分为两种:

sharutils - 利用 sharutils 工具对多个文件进行归档,同时生成一个 shell 脚步用于取出这些文件。该脚步中包含编码后的二进制或压缩包等文件的信息以及解码操作。这种方式需要依赖于 sharutils 工具,不够灵活。

自己通过 shell 命名实现 - 这种方式几乎能在所有的 Linux 平台运行,不依赖于特定工具。

为了简化操作,我采用了一个简单的 helloworld 工程目录,然后利用脚步对其进行解压并对该工程进行编译来模拟安装。

$ tree helloworld
helloworld/
main.c
Makefile
$ cat helloworld/main.c
#include <stdio.h>

int main()
{
printf("Hello world!\n");
return 0;
}

$ cat helloworld/Makefile
.PHONY: all

all: helloworld

helloworld: main.o
gcc -o $@ $^

%.o: %.c
gcc -o $@ -c $<

.PHONY: clean

clean:
rm -rf *.o helloworld


通过修改编译过程为安装过程即可实现脚步自动话安装。这里就只提供思路,不具体给出安装的示例了,希望大家可以举一反三。

Sharutils

Sharutils 是从许多文件中制作所谓的 shell 档案,准备通过电子邮件服务传输的工具。本文主要使用 shar 命令对多个文件进行归档,并调用其中的 shell 脚步执行基本的操作。在这之前需要首先安装 sharutils 工具集,在 Ubuntu 下使用如下命令:

$ sudo apt-get install -y sharutils


首先,我们对 helloworld 进行压缩

$ tar -czvf helloworld.tgz helloworld
helloworld/
helloworld/main.c
helloworld/Makefile


接着我们使用 shar 对压缩包进行编码并重定向到 compile.sh 脚步中

$ shar helloworld.tgz > compile.sh
shar: Saving helloworld.tgz (text)
$ cat compile.sh
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this arc
4000
hive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh11831
# Made on 2017-10-13 10:25 CST by <japin@ww-it>.
# Source directory was '/home/japin/WwIT/Codes/self-extract'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    344 -rw-rw-r-- helloworld.tgz
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums.  Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done

if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done

if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if   touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'

else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps.  Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= helloworld.tgz ==============
if test -n "${keep_file}" && test -f 'helloworld.tgz'
then
${echo} "x - SKIPPING helloworld.tgz (file already exists)"

else
${echo} "x - extracting helloworld.tgz (text)"
sed 's/^X//' << 'SHAR_EOF' | uudecode &&
begin 600 helloworld.tgz
M'XL(`+4CX%D``^W4STO#,!0'\%[W_HKGW&`35I-V:Z`;XG$7?UP%$4K6N6K6
MCJS#@_B_VQ9&"Z)#L,KP^SDT)0E)VO=>5K$QV4MFS>+<:8LH*#4I6ZDFHMGN
M.=*72@A/"K^8)SU/*(<GK9VH8;?-(\OL/$6;)/UBWJ'Q([6JX[^.DM35+>Q1
M!C@(QI_&7TJUC[]0OBKB[_LB<%BT<)8/_GG\3Y-4F]TBYMDV7R29N[H@2M*<
MRV08#.F5N+"Q1==RT)V7R<)5MIS<I]WAM!JU<;ZS*8LIO=%??PY\4Z/^KZ+G
M>)F8^,?W.%3_Q:U?U[\GROH/Q@+U_QO<V_G-]5W(D3%$Q2/D.B.(ZO>PNA'<
MC#J/6O,HX]XE]QZ(^FX6<M_5S?Z1YMZ,:+^T-G&4$E5-2!V[YI%=\IF;-;?Z
=Z_\```````````````````!PS-X!M-<!Y0`H``"4
`
end
SHAR_EOF
(set 20 17 10 13 10 23 22 'helloworld.tgz'
eval "${shar_touch}") && \
chmod 0664 'helloworld.tgz'
if test $? -ne 0
then ${echo} "restore of helloworld.tgz failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'helloworld.tgz': 'MD5 check failed'
) << \SHAR_EOF
78be4fbc5349d5028fb6cac108fa39d5  helloworld.tgz
SHAR_EOF

else
test `LC_ALL=C wc -c < 'helloworld.tgz'` -ne 344 && \
${echo} "restoration warning:  size of 'helloworld.tgz' is not 344"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0


从上面的 compile.sh 脚步中可以看到 shar 编码的文件信息。如果运行上面的 compile.sh 脚步,他只会将 helloworld.tgz 压缩包还原出来,若想要它自动编译,我们还需要在该脚步返回前添加必要的操作。

在上述 compile.sh 脚步最后的
exit 0
前加入下面的内容:

echo "x  - decompressing helloworld.tgz."
tar -xf helloworld.tgz
if test $? -ne 0
then
echo "decompress helloworld.tgz failed"
exit 1
fi

# Remove helloworld.tgz and auto-compile helloworld
echo "x - compiling helloworld"
rm helloworld.tgz && cd helloworld && make
if test $? -ne 0
then
echo "compile helloworld failed"
exit 1
fi


此时,运行 compile.sh 脚步即可将该脚步中的 helloworld.tgz 还原并解压为 helloworld 目录,同时将中间文件 helloworld.tgz 删除,随后对 helloworld 进行编译。

Shell 实现

该方式思想与 sharutils 相同,唯一不同的是 sharutils 对压缩包、二进制文件等可以采用不同的编码方式,而采用 shell 的实现则是直接将压缩包或二进制文件等写入到脚步中。这里我们同样采用 helloworld 来给出 shell 的实现方式。

首先,我们创建一个脚步用于解压 helloworld.tgz 文件:

$ cat shell-compile.sh
#!/bin/bash

main()
{
ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0;}' $0)
tail -n+$ARCHIVE $0 | tar xz >/dev/null 2>&1

cd helloworld && make

exit 0
}
main

# This line must be the last line of the file
__ARCHIVE_BELOW__


该文件中需要注意几点:

__ARCHIVE_BELOW__ - 用于标识该文件中的压缩文件数据,即 helloworld.tgz 的数据,并且该行只能在文件的末尾。

awk - 用于查找 helloworld.tgz 数据的开始行。

tail - 用于解析 helloworld.tgz 的数据并利用管道传递给 tar 进行解压。

exit - mian() 函数中的 exit 命令是不可少的。如果没有该命令,shell 脚步会继续执行后面的内容导致出错。

接着,我们就将 helloworld.tgz 的数据放置在该文件之后,这样就可以实现脚步自解压编译了

$ cat hell
b272
oworld.tgz >> shell-compile.sh


完成上述步骤之后,我们就可以通过 shell-compile.sh 获取到 helloworld 目录,并对 helloworld 进行编译。

参考

[1] 在shell中嵌入二进制文件

[2] Bash Self-Extracting Script
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: