Linux shell 命令和正则表达式

今天在Fedora20系统上编译VPP项目时遇到了一个问题,在最终编译并且使用rpmbuild生成的rpm安装包不正确,我们需要对生成rpm包的spec文件做一些小的修改。

 首先我们来看看VPP自带的生成rpm包的spec描述文件,该文件为${VPP_TOPDIR}/build-root/rpm/vpp.spec, 我们可以打开看看该文件的内容:

 其中在%install 段里面有如下的命令

 1 #
 2 # libraries
 3 #
 4 mkdir -p -m755 %{buildroot}%{_libdir}
 5 for file in $(find %{_vpp_install_dir}/*/lib* -type f -name '*.so.*.*.*' -print )
 6 do
 7     install -p -m 755 $file %{buildroot}%{_libdir}
 8 done
 9 for file in $(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e 's/^\.\///')
10 do
11     # make lib symlinks
12     ( cd %{buildroot}%{_libdir} && 
13           ln -fs $file $(echo $file | sed -e 's/\(\.so\.[0-9]\+\).*/\1/') )
14 done

 这里首先在%{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/')  &&
ln -sf $file $(echo $file | sed -e 's/\(\.so\).*/\1/') )
done

这样在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

posted @ 2016-06-13 23:10  亚洲舞王尼古拉斯赵四  阅读(2320)  评论(0编辑  收藏  举报