2010年12月26日 星期日
/usr/local/share/dvdwizard/dvdwizardrc
包括异常处理、创建按纽、建立目录和文件、清除临时目录等通用函数,关注的地方:
1、处理异常:
error_out()
{
cat << EOF
信息提示
EOF
if [ -e "$LOGFILE" ]; then
cat << EOF
Here are the last 10 lines of the log ($LOGFILE):
-------------------------------------------------------
EOF
tail -n 10 "$LOGFILE"
fi
exit 1
} >&2
包括 here 文档, 函数的输出重定向至标准错误输出
2、break N 跳出循环似乎包括 if... then ... fi 结构
while [ $i -lt $# ]; do
let "i+=1"
eval passed_arg=\${$i}
if [ "$passed_arg" == "-C" -o "$passed_arg" == "--config-file" ]; then
let "i+=1"
eval confFile=\${$i}
if [ -f "$confFile" -a -r "$confFile" ]; then
:
else
echo "Specified Config-File $confFile not found or no read permission. Aborting" >&2
exit 1
fi
break 2
fi
done
3、if 块、循环块等重定向的使用
if [ "$IM_VERSION3" \< "6.3.5" ]; then
echo "dvdwizard NEEDS at least ImageMagick Version 6.3.5 or higher"
echo "See $thisscript -h for more infos"
exit 1
fi >&2
for c in $TRANSLUCENT $TITLECOLOR $HEADCOLOR $TEXTCOLOR $NORMCOLOR $HICOLOR $SELCOLOR $ACTCOLOR; do
if [ "${c:0:1}" == "#" -o "${c:0:3}" == "rgb" ]; then
:
else
echo "$colorlist" | grep ^"${c}"$ >/dev/null
if [ $? -ne 0 ]; then
echo "Invalid color $c specified. Please check config file."
exit 1
fi
fi
done >&2
4、eval 的大量使用增加程序的灵活性
eval maxtxt=\"\$txt_$i\"
用法:这里$i 是个变量,如果不用 eval 命令,像这样: maxtxt=$txt_$i ,maxtxt 的值为$txt_变量的值(这里没有这个变量,所以值是空字符串)联接 $i 的值,那么 maxtxt 的值就只等于 $i 的值了。如: i=2; txt_2=test; echo $txt_$i # 2 而不是 test 了。加了 eval 后,会先计算 $i 的值是2,那么最后的结果就是 $txt_2 等于 test 了。
疑问:
能不能用面向对象的思维用 Bash 脚本编程呢?
如 Operator.sh
{
var1;
var2;
init();
do1();
do2();
destroy();
}
在主程序中调用这个 operator.sh
operator1=new operator.sh;
operator1.do1();
operator1.do2();
这样做有没有什么好处呢?有没有什么坏处呢?有必要吗?可以试试,把 dvdwizard 里面的这个通用函数库作成一个对象的类,里面的函数就是对象可以进行的操作,初始化就是读入配置文件,初始化各种变量;注销就是删除临时目录文件等后续操作。
当然,像真正的面向对象语言里面的继承、重载什么的就不要去尝试了。只使用单一的类来试。
参考资料: A New Object-Oriented Programming Language: sh https://www.usenix.org/publications/library/proceedings/bos94/full_papers/haemer.a
/usr/local/bin/dvdwizard
结构:
定义一些内部的函数,调用其它功能脚本和通用脚本(dvdwizardrc)里的函数
主要处理流程
关注的部分:
1、
if ! let x=$LOOP+0 ; then
if [ ! $LOOP -eq 0 ]; then
echo "Playback mode $LOOP (-L|--loop \$LOOP) is not numeric"
error_out
fi
fi
这里 if let x=$a ; 是用算术结果进行测试,如果 x=0 则测试为假,否则为真,但是如果没有 let 这个代表算术赋值的语句,不管x=0还是多少,都是当作字符串处理,除非 x 为空字符串,测试都为真。
2、
把目录下的文件名作为元素封装入一个数组中
eval cshots=( $(ls -QS "$BASEDIR/cpics/VTS$i") )
3、
写入分行字符串进入文本文件的用法(here 文档重定向到文件)
cat << EOF >> "$XMLFILE"
call vmgm menu entry title;
EOF
4、
指定优先级别运行程序同时记录日志和处理错误
nice -n $NICE dvdauthor -x "$XMLFILE" >> "$LOGFILE" 2>&1 || error_out
5、
读取大量参数的用法:
while [ -n "$*" ]; do
case "$1" in
-C|--config-file)
shift
# -C and it's following parm already processed in set_defaults()
shift
;;
-o|--output)
shift
DESTDIR="$1"
shift
;;
...
esac
done
因为命令调用的格式是 dvdwizard -C /path/to/configfile -o /path/to/outputfile ... 这样的形式,所以 shift 一次去掉参数选项,剩下的参数值读入变量,读入后,再 shift 一次去掉已经读了的参数值。
6、好像是一个重启进行过程中某一步骤的方法,有需要时再详细学习
case "$restart" in
dvdcpics)
call_dvdcpics
call_mk_vmgm
call_mk_vtsm
call_author
;;
mk_vmgm)
call_dvdcpics_upd
call_mk_vmgm
call_mk_vtsm
call_author
;;
mk_vtsm)
call_dvdcpics_upd
call_mk_vtsm
call_author
;;
...
从这个脚本可以看出,非面向对象的写法确实难以控制代码量比较大的程序,如这个 dvdwizard 里面读入参数的脚本就用了 1078-754=324 行代码!而且,我看其中获取视频片断信息的代码出现在了这个主脚本中,同时也有一个专门获取片断信息的脚本 mpgprobe ,为什么不用一个类比如叫作 detector 的,要获取片断信息时统一叫这个 detector 的对象去做然后报告相关的信息。
所以,对于大型的 shell 脚本程序,可以尝试用面向对象的方式来编程,应该可以增加代码的可读性和程序的健壮性的。尽量使用短脚本来完成特定的任务。
然后,从# Main Processing 开始,主程序调用的脚本文件依次是:
check_tools(dvdwizardrc)
checkParms(dvdwizard)
mk_tmpdvd(dvdwizardrc),里面调用了chaptercheck 脚本
然后依次是调用dvdwizard 里面的函数
call_dvdcpics(dvdcpics)
call_mk_vmgm(mk_vmgm)
call_mk_vtsm(mk_vtsm)
call_author (dvdwizard)
cleanup_tmpdir(dvdwizardrc)
可以看出,里面的调用关系比较混乱,脚本之间互相调用,主控脚本里有的调用自身的函数,有的调用脚本,等等。耦合度太高。应该可以有办法把这些脚本用面向对象的方式好好组织一下。
大概把 dvdwizard 的源码浏览了一下,里面的具体用法没有细看,以后有必要时再参考。
A New Object-Oriented Programming Language: sh https://www.usenix.org/publications/library/proceedings/bos94/full_papers/haemer.a
浏览了一下,不太明白,以后有需要时再参考。
注意:把用 Shell 写的程序封装得好一点,耦合度小一些,每个脚本完成的功能单一一些,维护容易一些。用面向对象的思维方式来写 Shell 脚本。