获取程序所在目录代码解析

我觉得,Shell对比其他的编程语言,最大的优势在于每个字符都包含有极大的信息量,其他语言可能要很长很多行代码才能实现的功能,Shell代码一行就能实现;而且一个可用的Shell脚本里几乎每个字符可能都无法更改,一旦更改,就会造成整个脚本不可用(当然,变换实现思路修改了整段代码的除外)。

今天下午熟悉线上环境的巡检脚本,看到这样一段代码,感觉挺有意思的,也很好的印证了我上面的观点。特意择出来分析一下这段代码包含的一些东西。这段代码网上几乎随处可见,而且极其精炼,几乎是改无可改了,呵呵。

这段代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
# Resolve links - $0 may be a symbolic link
PRG="$0"
while [ -h "$PRG" ]; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '.*/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done
PRGDIR=`dirname "$PRG"` #所执行程序所在的目录

第一行是注释,很容易理解;最后一行就是这段脚本所要实现的目标,即获取一个可执行脚本程序所在的目录。

首先从脚本第一行可用代码来解释:PRG=”$0”,这里的PRG指的就是可执行脚本的文件名,当然也可能是指向这个脚本的软链接文件,这个在下面会做判断,暂且按下不表。先看软链接,那么什么又是软链接呢?

Linux下的链接分为两种:硬链接(Hard Link)和软链接(Symbolic Link),我们经常见到的link命令,就是用来创建Linux链接的,link默认创建的是硬链接,加上-s参数就是创建软链接。文件链接是Linux文件系统的一个重要特征。在这个脚本里指的就是软链接。

硬链接引用的文件在文件系统的物理索引,即inode;所以不管你是删除或者移动源文件,硬链接都不会被破坏,它引用的并不是文件在文件结构里的位置。这样的话,用户就不必需要拥有对源文件的访问权限,就防止了误删,只有所有的硬链接引用都被删除之后,才能把文件真正删除。这相当于是为源文件创建了一个别名,实际上源文件和链接文件是同一个文件,用ls -i查看其inode值,可以发现他们是一样的。

软链接有点像Windows里的快捷方式,但又不一样。跟硬链接一样,编辑或者执行软链接文件,就等于是编辑或者执行了源文件。但是软链接文件与源文件是不同的两个文件,软链接文件只是指向源文件的一个指针,删除了软链接文件对源文件没有任何影响,删除了源文件,软链接文件也就没有了任何意义,cat查看的时候会提示文件不存在。另,软链接可以指向文件夹,硬链接却不可以。

紧接着是一个while循环,while [ -h “$PRO” ]; do …… done,参数-h的意思就是判断$PRO是否存在和$PRO是否是一个软链接文件,这个循环的意思就是当$PRO存在并且$PRO是一个软链接文件的时候,执行do …… done之间的脚本。

接下来就是expr这个函数了。这是Linux里一个很重要的命令,一般用于整数值计算,也可用于字符操作。

  1. 整数计算里,运算符两边注意加空格即可,”*”符号要加”\”转义符;
  2. 循环里的expr用于增量计算。循环初始化为0,然后循环值加1,如下面的例子,从(expr)命令接受输出并将之作为输入的循环变量;
  3. expr同样可以进行检测是个字符是否是数字;
  4. 模式匹配。在这个脚本里,expr的作用就是模式匹配,符号就是冒号”:”。
1
link=`expr "$ls" : '.*-> \(.*\)$'`

这行代码的返回的就是”->;”后面的字符串,实际上也就是软链接指向的真实的文件名;

1
2
if expr "$link" : '.*/.*' > /dev/null; then
PRG="$link"

这两行是为了判断$link是否是以”/“开头;

1
PRGDIR=`dirname "$PRG"`

这行就是最后我们所想要的结果了。

至此,这段代码就分析完了。Shell里符号太多,不熟悉的话一眼看上去感觉好蒙啊,实际上每个字符都有很大的信息量,主要还是对这些字符的熟悉程度。