Shell 自解压安装脚步

来源:互联网 发布:随机抽取名字软件 编辑:程序博客网 时间:2024/05/29 10:41

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

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

  • sharutils - 利用 sharutils 工具对多个文件进行归档,同时生成一个 shell 脚步用于取出这些文件。该脚步中包含编码后的二进制或压缩包等文件的信息以及解码操作。这种方式需要依赖于 sharutils 工具,不够灵活。
  • 自己通过 shell 命名实现 - 这种方式几乎能在所有的 Linux 平台运行,不依赖于特定工具。

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

$ tree helloworldhelloworld/  main.c  Makefile$ cat helloworld/main.c#include <stdio.h>int main(){    printf("Hello world!\n");    return 0;}$ cat helloworld/Makefile.PHONY: allall: helloworldhelloworld: main.o        gcc -o $@ $^%.o: %.c        gcc -o $@ -c $<.PHONY: cleanclean:        rm -rf *.o helloworld

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


Sharutils

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

$ sudo apt-get install -y sharutils

首先,我们对 helloworld 进行压缩

$ tar -czvf helloworld.tgz helloworldhelloworld/helloworld/main.chelloworld/Makefile

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

$ shar helloworld.tgz > compile.shshar: 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 archive, 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=truefiecho=echosave_IFS="${IFS}"IFS="${IFS}:"gettext_dir=locale_dir=set_echo=falsefor dir in $PATHdo  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  fidoneif ${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"  fifiIFS="$save_IFS"if (echo "testing\c"; echo 1,2,3) | grep c >/dev/nullthen if (echo -n test; echo 1,2,3) | grep n >/dev/null     then shar_n= shar_c=''     else shar_n=-n shar_c= ; fielse shar_n= shar_c='\c' ; fif=shar-touch.$$st1=200112312359.59st2=123123592001.59st2tr=123123592001.5 # old SysV 14-char limitst3=1231235901if   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 andinstalling GNU '\''touch'\'', distributed in GNU coreutils...'  echofirm -f ${st1} ${st2} ${st2tr} ${st3} ${f}#if test ! -d ${lock_dir} ; then :else ${echo} "lock directory ${lock_dir} exists"     exit 1fiif mkdir ${lock_dir}then ${echo} "x - created lock directory ${lock_dir}."else ${echo} "x - failed to create lock directory ${lock_dir}."     exit 1fi# ============= 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.tgzM'XL(`+4CX%D``^W4STO#,!0'\%[W_HKGW&`35I-V:Z`;XG$7?UP%$4K6N6K6MCJS#@_B_VQ9&"Z)#L,KP^SDT)0E)VO=>5K$QV4MFS>+<:8LH*#4I6ZDFHMGNM.=*72@A/"K^8)SU/*(<GK9VH8;?-(\OL/$6;)/UBWJ'Q([6JX[^.DM35+>Q1M!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+GM>)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`endSHAR_EOF  (set 20 17 10 13 10 23 22 'helloworld.tgz'   eval "${shar_touch}") && \  chmod 0664 'helloworld.tgz'if test $? -ne 0then ${echo} "restore of helloworld.tgz failed"fi  if ${md5check}  then (       ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'helloworld.tgz': 'MD5 check failed'       ) << \SHAR_EOF78be4fbc5349d5028fb6cac108fa39d5  helloworld.tgzSHAR_EOFelsetest `LC_ALL=C wc -c < 'helloworld.tgz'` -ne 344 && \  ${echo} "restoration warning:  size of 'helloworld.tgz' is not 344"  fifiif rm -fr ${lock_dir}then ${echo} "x - removed lock directory ${lock_dir}."else ${echo} "x - failed to remove lock directory ${lock_dir}."     exit 1fiexit 0

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

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

echo "x  - decompressing helloworld.tgz."tar -xf helloworld.tgzif test $? -ne 0then    echo "decompress helloworld.tgz failed"    exit 1fi # Remove helloworld.tgz and auto-compile helloworldecho "x - compiling helloworld"rm helloworld.tgz && cd helloworld && makeif test $? -ne 0then    echo "compile helloworld failed"    exit 1fi 

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


Shell 实现

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

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

$ cat shell-compile.sh#!/bin/bashmain(){    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__

该文件中需要注意几点:

  1. __ARCHIVE_BELOW__ - 用于标识该文件中的压缩文件数据,即 helloworld.tgz 的数据,并且该行只能在文件的末尾。
  2. awk - 用于查找 helloworld.tgz 数据的开始行。
  3. tail - 用于解析 helloworld.tgz 的数据并利用管道传递给 tar 进行解压。
  4. exit - mian() 函数中的 exit 命令是不可少的。如果没有该命令,shell 脚步会继续执行后面的内容导致出错。

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

$ cat helloworld.tgz >> shell-compile.sh

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


参考

[1] 在shell中嵌入二进制文件
[2] Bash Self-Extracting Script