今天在Fedora20系統上編譯VPP項目時遇到了一個問題,在最終編譯並且使用rpmbuild生成的rpm安裝包不正確,我們需要對生成rpm包的spec文件做一些小的修改。
首先我們來看看VPP自帶的生成rpm包的spec描述文件,該文件為${VPP_TOPDIR}/build-root/rpm/vpp.spec, 我們可以打開看看該文件的內容:
其中在%install 段裡面有如下的命令
1 # 2 # libraries這裡首先在%{buildroot}目錄裡面創建lib目錄,然後調用install命令將編譯出來的所有的VPP項目的lib文件拷貝到%{buildroot}目錄裡面的lib目錄裡面。
現在我們來看9~14行的一個 for循環命令,該命令主要是完成這樣的一個功能:將%{buildroot}目錄裡面的lib目錄裡面的所有的庫文件建立一個軟鏈接文件。
比如:假如現在在%{buildroot}目錄裡面的lib目錄裡面有一個庫文件libvnet.so.18.0.0, 那麼上述for循環完成之後就在同一目錄下建立了一個軟鏈接文件libvnet.so.18並指向libvnet.so.18.0.0庫文件。
現在我們來一步步的分析上面的9~14行的這個for循環語句的工作過程,我們假設在/usr/lib64/目錄下有2個庫文件,一個是libvpp.so.0.0.0, 一個是libvnet.so.18.0.0.
首先我們來看第9行的for語句後面的一個shell命令:
$(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e 's/^\.\///')
那麼等價於執行下面的一條命令:
$(cd /usr/lib64/ && find . -type f -print | sed -e 's/^\.\///')
這條命令是兩條命令組合而成,首先執行cd /usr/lib64/切換工作目錄到/usr/lib64/目錄下,然後執行後續的find命令。
隨後執行 find . -type f -print 命令的結果如下:
./libvpp.so.0.0.0
./libvnet.so.18.0.0
這裡find命令的查找輸出結果前面都帶有路徑的。然後將find命令查找的輸出結果通過管道重定向到sed命令中,再來分析後面的sed命令。
sed -e 's/^\.\///': 查找行首以./開頭的行並將./替換為空, 這裡^表示匹配行首, 後面有兩個轉義符分別轉義"點(.)"和"斜槓(/)", s動作表示查找替換
那麼經過sed命令處理之後的find查找結果就變為如下的結果了:
find . -type f -print | sed -e 's/^\.\///' 經過sed處理後的find查找結果全部去掉了前面的路徑了,該命令最終結果如下了:
libvpp.so.0.0.0
libvnet.so.18.0.0
至此,for循環中的file變量值就有了,file變量的值是集合{libvpp.so.0.0.0, libvnet.so.18.0.0}了。
下面我們再來分析for循環中的循環體語句。
( cd %{buildroot}%{_libdir} && ln -fs $file $(echo $file | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') )
我們可以在現在舉的這個例子裡面將上面的命令進行替換展開:
( cd /usr/lib64/ && ln -fs libvpp.so.0.0.0 $(echo libvpp.so.0.0.0 | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') )
( cd /usr/lib64/ && ln -fs libvnet.so.18.0.0 $(echo libvnet.so.18.0.0 | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') )
我們具體來分析其中的sed命令,這裡有一個正則表達式我們假設將正則表達式展開,那麼得到如下的一個sed命令:
$(echo libvpp.so.0.0.0 | sed -e 's/(.so.[0-9]+).*/\1/')
注意:上述sed命令的正則表達式沒有加轉義字符啊。
(.so.[0-9]+).* // (...)表示匹配子串並保存匹配的字符,[0-9]表示匹配1個數字字符,[0-9]+表示匹配1個或者多個數字字符,.表示匹配一個非換行符的任意字符,*表示匹配0個或者多個字符
\1//表示在這裡直接引用前面查找匹配子串時保存的匹配字符
經過上面的分析,我們可以得出下面的shell命令運行的結果:
$(echo libvpp.so.0.0.0 | sed -e 's/(.so.[0-9]+).*/\1/') //得到的結果字符串是: libvpp.so.0
$(echo libvnet.so.18.0.0 | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') //得到的結果字符串是:libvnet.so.18
然後調用的ln -sf命令直接就創建了2個軟鏈接文件:
libvpp.so.0-------->libvpp.so.0.0.0
libvnet.so.18--------->libvnet.so.18.0.0
雖然上面的shell命令創建了2個軟鏈接文件,但是我們知道在Linux下程序引用動態鏈接庫文件的時候一般習慣性使用-lvpp, -lvnet等來指定,這樣一來依賴的庫文件形式就是libvpp.so和libvnet.so了,而不是libvpp.so.0和libvnet.so.18了。
因此我們還需要創建2個軟鏈接文件libvpp.so和libvnet.so, 也分別軟鏈接到libvpp.so.0.0.0和libvnet.so.18.0.0。
我們只需要將上面的for循環修改一下即可,我們修改如下:
for file in $(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e 's/^\.\///') do # make lib symlinks ( cd %{buildroot}%{_libdir} && ln -fs $file $(echo $file | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') &&這樣在for循環執行完之後,就在/usr/lib64目錄裡面分別創建了4個軟鏈接文件了,其關系如下:
libvpp.so-------->libvpp.so.0.0.0
libvpp.so.0-------->libvpp.so.0.0.0
libvnet.so--------->libvnet.so.18.0.0
libvnet.so.18-------->libvnet.so.18.0.0
具體的關於sed命令的使用和正則表達式的詳細說明,請參考如下的links:
http://man.linuxde.net/sed
http://www.infoq.com/cn/news/2011/07/regular-expressions-6-POSIX