shell编程实战——Learn By Example

shell编程实战——Learn By Example
2010-11-16 星期二 清冷
一直都是在命令行上使用shell命令,但是却没有写过shell脚本,今天刚好要将国际站的所有代码拉到本地grep看修改点。人肉肯定是不行的,分支实在太多了,写个shell脚本吧^_^
svncotrunk.sh
#!/bin/bash

### 从svn上将指定的SVN URL递归将所有应用的trunk(有时间改成支持pattern)分支co到本地目录 ###
# 例如: svnco http://svn.alibaba-inc.com/repos/ali_intl/apps/intl-biz/escrow/ destdir(目标根目录,默认是当前目录)
#
#输入:svnurl
#操作:将给定URL的所有应用拉到本地某个目录(默认是当前目录)
#算法:
# 如果svnurl包含trunk,则直接svn co svnurl substr(svnurl)/trunk
# 否则如果svnurl包含branch/tags,则直接忽略(因为branch/tags分支太多了,需要额外信息明确要co的分支)
# 否则,说明这是在上层目录中,对svnurl的每个子目录,递归做上面的操作(递归终止条件:svnurl含有branch/tags/trunk 或者 > maxdepth=5)

## Shell脚本中的函数必须先定义后调用,一般把函数定义都写在脚本的前面,把函数调用和其它命令写在脚本的最后
## 参数:当一个函数被调用时,脚本程序的位置参数$*、$@、$#、$1, $2, ..等会被替换成函数的参数。这个跟shell的命令行参数是一样的处理方式。当函数执行完毕,这些参数会恢复它们先前的值。
## 局部变量:可以使用local关键字在shell函数中声明局部变量,局部变量将局限在函数的作用范围内。
## 返回值:如果在函数里没有使用return命令指定一个返回值,函数返回的就是执行的最后一条命令的退出码; 如果return后面跟一个数字则表示函数的Exit Status。shell的函数返回值只能是整数,作为函数的退出状态,and this exit status is assigned to the variable $?.

## 检查给字符串是不是以某个子字符串结尾
end_with()
{
    local src=$1
    local pattern=$2

    ret=`echo $src | grep $pattern$`

    if [ -z "$ret" ] #not found
    then
        return 1
    else
        return 0
    fi
}

## 检查给定字符串是不是包含指定子字符串
# 这里用来判断是否包含/trunk/tags/branches
contains()
{
 	local src=$1
    	local pattern=$2
	
	ret=`echo $src | grep $pattern`

	if [ -z "$ret" ] #not found
	then
		return 1
	else
		return 0
	fi
}

## 从svnurl中获取保存本地的目录名
## 如:http://svn.alibaba-inc.com/repos/ali_intl/apps/intl-biz/escrow/trunk/ ==> ali_intl/apps/intl-biz/escrow/
##    http://svn.alibaba-inc.com/repos/ali_intl/apps/intl-biz/escrow/trade/trunk/biztrade/ ==> ali_intl/apps/intl-biz/escrow/trade/biztrade/
get_path_from_svnurl()
{
	local svnurl=$1
	echo $svnurl | sed 's#http://svn.alibaba-inc.com/repos/##' | sed 's#trunk/##'
}

get_svncourl_from_svnurl()
{
	local svncourl=$1
	svncourl=${svncourl%%trunk*} 
	local TRUNK="trunk/"
	echo $svncourl$TRUNK
}

### 从svn上将指定的SVN URL递归将所有应用的trunk(有时间改成支持pattern)分支co到本地目录 ###
# 例如: svnco http://svn.alibaba-inc.com/repos/ali_intl/apps/intl-biz/escrow/ destdir(目标根目录,默认是当前目录)
function svncotrunk(){
	#为了方便后面的判断和子svnurl拼接
	if end_with $1 "/"; then
		local svnurl="$1"
	else
		local svnurl="$1/"
	fi
        
	local depth=$2		

	# 超过递归最大层次,退出	
	if [ $depth -lt 0 ]; then
		datetime=`date +"%y-%m-%d %H:%M:%S"`
		echo "$datetime ERROR: Recursive depth exceed $MAX_DEPTH for $svnurl"
		echo "$datetime ERROR: Recursive depth exceed $MAX_DEPTH for $svnurl" >> $SVN_CO_LOG
		return 1
	fi

    	# 注意Shell中的函数调用不写括号,否则会报错;参数之间是用空格隔开,而不是逗号
	# Remember that the exit status of zero is a true condition in shell programming.
	if contains $svnurl "/archived/"; then
	    echo "Ignore archived url"
	elif contains $svnurl "/trunk/"; then
            # 从svnurl中截取以trunk/结尾的那部分url 
	    local svncourl=`get_svncourl_from_svnurl $svnurl`
	    # 保存分支到一个统计文件
	    datetime=`date +"%y-%m-%d %H:%M:%S"`
	    echo "$datetime==>$svncourl" >> $SVN_CO_LOG
	    local path=`get_path_from_svnurl $svncourl`
	    svn co $svncourl $path 
 	elif contains $svnurl "/branches/" || contains $svnurl "/tags/" || contains $svnurl "/milestone/"; then
	    echo "This is a branch or tag or milestone, we don't deal with braches or tags or milestone right now!"
	else
 	    subdirs=`svn ls $svnurl`
	    for subdir in $subdirs; do
                subsvnurl=$svnurl$subdir
		# Recursive
   	        svncotrunk $subsvnurl `expr $depth - 1`  #注意减号两旁必须有空格,否则会被当作字符串拼接
	    done
	fi
}

MAX_DEPTH=8

# we have less than 1 argument. Print the help text:
if [ $# -lt 1 ]; then
cat<< HELP

NAME
	svncotrunk -- 从svn上将指定的SVN URL递归将所有子分支的trunk分支co到本地目录
SYNOPSTS
	svncotrunk svnurl [svnco.log path] [depth=8]
DESCRIPTION
	从svn上将指定的SVN URL递归将所有子分支的trunk分支co到本地目录, 将svn co分支保存在svnco.log文件中(如果没有指定文件,默认是当前目录下的svnco.log)

例如: svncotrunk http://svn.alibaba-inc.com/repos/ali_intl/apps/intl-biz/escrow/ cohistory.log


HELP
   exit 0
elif [ $# -eq 1 ]; then
   SVN_CO_LOG="svnco.log"
   svncotrunk $1 MAX_DEPTH
elif [ $# -eq 2 ]; then
   SVN_CO_LOG=$2
   svncotrunk $1 $MAX_DEPTH
elif [ $# -ge 3 ]; then
   MAX_DEPTH=$3
   svncotrunk $1 $MAX_DEPTH
fi
关于函数和返回值
法一:使用 return 这个命令,把函数中某个数值返回,其实是使用Exist Status作为返回值,使用$?接收返回值
法二:在函数中使用 echo 输出想要返回的结果,使用`function call`接收函数输出结果。
Output can be in the form of stdout or a return code value or both.
这两者往往是结合在一起的,如果你不想显示在stdout中显示函数的echo值,可以采用function > /dev/null将函数的echo输出到黑洞中。
参考资料:
4. Chapter 6. Exit and Exit Status http://www.faqs.org/docs/abs/HTML/exit-status.html

相关推荐