Linux下安裝JDK的時候,Sun公司為JDK6的linux版本提供了一個shell的安裝包,用起來特別的好用,基本上和在Windows下安裝軟件沒什麼兩樣,shell文件執行之後,幾乎一切都系都設置好了,不用我們自己再動手設置PATH和JAVA_HOME,可是一個shell文件中是如何把二進制代碼包含進來的呢?
再例如淘寶為linux開發的淘寶插件,其實也是一個shell文件,但是執行這個shell文件之後,會安裝很多二進制的東西,同樣的問題,Shell只是文本文件,其中的二進制是怎麼來包含進來的呢?
從文本文件轉換出可執行文件,通過編譯器把源程序編程成可執行程序當然是可以的,但是前面提到的哪兩種情況都不是這樣做的。原因有兩個,第一對於大的項目來說,編譯需要的時間比較長,環境比較復雜;第二,更加重要的是,這樣做其實和從源代碼編譯程序沒什麼兩樣,對於不想讓用戶看到自己的源碼的商業軟件來說,這顯然是不可取的。
我們看看一般的程序是如何發布他們的二進制包的。
一般的程序,無非是把二進制和必要的文檔壓縮到一個壓縮文件中,然後通過README文檔的解釋程序的運行依賴什麼樣的假設,然後,你就可以把程序自行的移動到你想移動的地方去了。
很多時候,我們把程序放到任何地方都是可以運行的,但是程序可以運行,並不是說我們已經完成了程序的安裝,舉個例子來說,如果我們解壓JDK的二進制包之後,直接把程序移動到一個地方,然後把對應的bin目錄添加到PATH中就可以執行JDK提供的一系列工具了。
但是如果我們安裝其他的依賴與JDK的程序的時候,比如TOMCAT,那麼就會有問題。因為我們只是在PATH中加入了JDK的bin目錄而沒有制定JAVA_HOME這個環境變量,所以TOMCAT很可能會不能運行。
再比如我們使用man命令來查看一個程序的手冊,一般情況下二進制包中也會包含man文檔的,但是如果我們只是把解壓的二進制包的路徑添加到了PATH中,還不能在man中找到對應的文檔。
也正是因為這樣的原因吧,所以很多的二進制包的發布是使用deb或者rpm包來發布。安裝的時候少了很多的煩惱。要制做deb或者rpm包當然是需要學習成本的,而且deb和rpm也只能在對應的linux發行版中使用,如果想要為所有的Linux發行版都提供一個安裝文件,那麼使用shell文件來做無疑是最好的辦法。
Shell的學習成本低,而且對linux平台來說有通用,那麼是如何做到的呢?
想想我們在手動安裝的情形,無非是把壓縮的二進制包解壓,移動到特定的目錄下,在PATH變量中添加二進制包的可執行文件的路徑等等工作。首先我們把二進制包壓縮文件和shell文件分開。這樣一來,shell中只要完成解壓,然後把解壓後的目錄移動到指定的目錄中去,設置各種各樣的環境變量然後就完成了工作了。
但是我們在如何把壓縮文件和這個shell解壓之後要執行的命令的shell文件一起放到一個shell文件中呢?
要做到這一點,首先這個shell文件中,要有可以解壓的二進制內容,其次,這個shell要做的工作就是,把二進制內容,解壓,然後把原來手動做的工作在這個shell中用命令完成。再用shell寫個腳本完成一些手動完成的工作,這個任務比較容易,所以制作shell安裝包的難點就是如何在其中包含二進制內容了。
如何在一個文本文件中記錄二進制的內容呢?
這個問題早就被解決了。答案就是使用Base64編碼。在linux下就有base64 這個命令程序就是來做這個工作的。base64可以把文件進行base64的編碼,輸出的標准輸出中去或者把文件中的Base64編碼的內容解碼。命令base64除了可以對文件的內容做Base64的編解碼外,也可以對標准輸入中的數據進行Base64編輯碼。
有了這些預備的知識,那麼我們就可以看看具體的如何來做shell的二進制發布包了。
首先假設我們要發布的文件都放在名為test的當前文檔中。
把要發布的文件打包
tar zcf test.tar./test
對打好的二進制包做Base64編碼
base64 ./test.tar> test_base64.txt
准備安裝文件的shell文件
test_base64="";#test_base64中的所有內容
echo $test_base64|base64 -d >test.tar
tar zxf test.tar
rmtest.tar
#其他的安裝代碼
寫到這裡,我們已經把道理將明白了。但是可不可以寫一個shell程序,專門來生成這樣的發布包軟件呢?當然可以。下面是我寫的這個打包程序的源代碼。
function mkpackage(){
target_dir=$1
felow_install_shell_command_file=$2
echo"tar -zcf ._test_dir.tar.gz $target_dir"
tar-zcf ._test_dir.tar.gz $target_dir
base64 ._test_dir.tar.gz >._base64
rm._test_dir.tar.gz
printf"test_base64=\"">install.sh
while IFS='' read -r line ||[[-n "$line"]];do
printf"$line\\">>install.sh
printf"n">>install.sh
done<./._base64
rm._base64
echo"\"">>install.sh
echo'printf $test_base64|base64 -d >._temp.tar.gz;'>>install.sh
echo'tar zxf ._temp.tar.gz'>> install.sh
echo'rm ._temp.tar.gz'>>install.sh
if[[-e $fellow_install_shell_command_file ]];then
cat $fellow_install_shell_command_file >>install.sh
fi
chmod+x install.sh
}
function usage(){
echo"usage:"
echo" $1 test_dir [the_command.sh]"
}
if[[ $#!=0]]
then
mkpackage $1 $2
else
usage $0
fi
這個程序接受兩個輸入參數,第一個表示要打的程序包目錄,第二個是解壓後,安裝動作的shell腳本文件。最後這個程序生成一個名為 install.sh 的文件。執行 install.sh 之後,會首先得到加壓的程序包內容,然後執行第二個參數指定的 shell 腳本的內容。這樣我們就做了用Shell發布二進制文件的一個打包程序。當然了,在最後發布之前,還需要把這個shell壓縮一下。以便消除因為Base64編碼而帶來的文件長度的變大的影響。