全部博文(330)
分类: LINUX
2011-07-06 15:46:05
先让大家看一个脚本:
#!/bin/sh
start_time=${1:-19:00} amount=${2:-1000} total_runtime=${3:-1} runinterval=${4:-1}
BASE="/home/wolf/ezra/deploy/target/bundle-ear" LOCAL_CLASSPATH=`echo $BASE/*.jar | tr ' ' ':'` CLASSPATH="/usr/repos/repository.external/java/j2ee/j2ee-1.4.jar:$LOCAL_CLASSPATH"
if [ -n "`ps -ef | grep java |grep task`" ];then echo "task has been run before, refuse to run another task instance." >> /home/wolf/output/daemon/usr/task.log exit -1; fi
java -DtaskName=task -cp $CLASSPATH -Dapplication.codeset=GBK -Ddatabase.codeset=ISO-8859-1 -Xms64m -Xmx1024m -server com.daemon.offer.Task $start_time $amount $total_runtime $runinterval &
|
看到什么问题没?脚本是没有问题的,对吧。只是如果摆放程序的位置发生变化,至少有几个地方是需要修改的。这包括程序所在目录,第三方类库的目录以及日志文件目录的信息。
那么是不是就留着让它继续这样下去吗?当然是可以的,只要你能忍受无休止的重复劳动,而本来你可以用做这些无聊的事情时间去做更多有意义的事情!
假设我们有个项目属性文件可以提供这些信息,那么我们现在要做的事情就是想办法从这个属性文件里面将这些配置读取出来。读取配置是容易的,因为配置里面都是一些键值对。这个用grep或者sed以或者用awk都能比较简单方便的取出来。可是这里有个难题,怎么解决属性值嵌套问题呢?例如下面的代码:
deployhome = ${homepath}/deploy homepath = /usr/ ${project}/416 project=anthrex |
好了,先解决第一个问题,看看以下段代码:
function getValue() { echo "`sed -e 's/^#.*//' < $2| sed -n 's/.*'$1'[\ ]*=[\ ]*//p'`" }
JAVA_HOME="`getValue javahome $properties`"; SECOND_REPOSITORY="`getValue project $properties`" THIRD_REPOSITORY="`getValue external $properties`" HOME_PATH="`getValue homepath $properties`"; OUTPUT="`getValue output $properties`" |
bash shell里面是允许有函数的,与Java的函数不同之处就是,方法参数是不需要声明的。上面这段脚本里面,核心是getValue函数,不用声明函数参数,调用时直接指定就可以的。这个函数很简单,利用sed对输入的每行字符做了两个替换动作。
sed -e 's/^#.*//' |
命令中字符s告诉sed要进行替换操作。/是分隔符,第一个分隔符后面指定了^#.*,要替换的行就是注释的行,第二和第三个是空字符。也就是先忽略掉注释的行。
sed -n 's/.*'$1'[\ ]*=[\ ]*//p' |
这个与第一个sed命令不同是-e变成了-n,就是默认不打印所匹配的模式使用了这个参数,在整个命令的后面通常都会跟p打印指令。-e是运行脚本。这个命令是要将匹配的键值找出来,并且把空格去掉。如果$1是java.home,那么这个命令就可能找到java.home = /usr/java这一行,最后得到/usr/java。
好了,通过上述改造,脚本只需要输入项目Properties文件文件就可以了,其他参数自动生成。
这个问题是完全结束了,可是要是属性值里引用了其他键值,例如
upload.temp = ${output}/temp |
,这个脚本就没辙了。要是也能做到像Spring里面的PropertiesConfigure实现那样该多好啊!
显然,要简单的想用shell或者sed来实现这个功能已经不容易了,起码会很费劲。这里我想起了awk里面的Associative Array,关联数组,说白了这个与Java里面的Map非常类似。利用这特点就可以将属性值里面的引用扩展了。
cat $1 | sed -e 's/^#.*//g' -e 's/[ ]*//g' | gawk -f ./expan.sh > properties properties=./properties
function getValue() { sed -n 's/.*'$1'[\ ]*=[\ ]*//p' < $2; }
JAVA_HOME="`getValue javahome $properties`"; SECOND_REPOSITORY="`getValue project $properties`" THIRD_REPOSITORY="`getValue external $properties`" HOME_PATH="`getValue homepath $properties`"; OUTPUT="`getValue output $properties`" |
这个脚本与之前的脚本最大的不同在于引入了awk,先把注释行替换才丢给awk处理。再来看看这个expan.sh
#!/bin/gawk -f #written by Ginge BEGIN{ FS="="; } { if(NF ==2) { map[$1]=$2; } } END { expan(map);
for(i in map){ print i"="map[i]; } }
function expan(map){ for (i in map) { val=map[i]; left=index(map[i], "{"); right=index(map[i],"}"); subkey=""; if(left > 0 && right > 0){ subkey=substr(map[i],left+1,right-left-1); if(length(map[subkey]) > 1){ val=map[subkey]""substr(map[i],right+1,length(map[i])); map[i]=val; if(index(map[i],"{") > 0) expan(map); } } } } |
看上去还是很简单的吧,第一行声明了这是一个gawk(用gawk是为了要使用它的用户自定义function特性)脚本。
Awk默认以空格来分离每行的输入,上述脚本在BEGIN代码块指明了用=来分离。这样
javahome=/user/Java/jdk1.5.0_17 |
这行输入就被分成了javahome和/user/Java/jdk1.5.0_17,用位置参数可以访问到这些分离之后的值。例如$1的值是javahome,$2是/user/Java/jdk1.5.0_17,以此类推。
BEGIN和END代码块是可选的,这些代码只调用一次,中间中括号之间的map[$1]=$2就是关联数组了;它对每个输入都是执行一次的。结合BEGIN和中间的代码块,我们就做到了map[javahome]= /user/Java/jdk1.5.0_17这样的效果。如何扩展属性看看那个附件的expan函数就知道了。
到这里改造完成了。
deployhome = ${homepath}/deploy homepath = /usr/ ${project}/416 project=anthrex |
通过脚本改造后可以得到这样的结果
deployhome= /usr/ anthrex /416/deploy homepath= /usr/ anthrax/416 project=anthrex |
到此我们实战了sed的简单用法和awk的高级功能关联数组,看来shell脚本还是比较好玩的。