尽管在近两年使用过 Unix 的一些人可能尝试过 shell 脚本编程,但是他们很可能只是研究操作系统的细节,并不精通 shell 脚本编程 。本文针对那些希望进一步了解 shell 脚本,并开始编写更高级脚本的读者 。本文提供脚本编程的基础知识,包括如何简化脚本、如何尽可能保持脚本的灵活性、如何编写干净的脚本、在脚本内编写注释以及调试脚本 。
保持简单
在人们学习如何编写 shell 脚本时,常常遇到的一个问题是,重复他们在另一个脚本中已经做过的工作 。他们其实不需要复制原来的脚本并修改几个硬编码值,只需创建一个函数来处理两个脚本的重复部分 。创建集中的函数还可以促进标准化,帮助创建统一的脚本 。如果一个函数在脚本的一个部分工作正常,那么它在脚本中的其他地方也会正常工作 。
例如,清单 1 所示的脚本应该浓缩和简化为更简单、更干净的程序 。
清单 1. 可以简化的脚本示例
#!/usr/bin/ksh
if [[ $# -lt 2 ]]
then
echo "Usage: ${0##*/}
exit 0
fi
if [[ ! -f "${1}" ]]
then
echo "Unable to find file '${1}'"
exit 1
fi
if [[ ! -r "${1}" ]]
then
echo "Unable to read file '${1}'"
exit 2
fi
gzip ${1}
ls -l ${1}.gz
if [[ ! -f "${2}" ]]
then
echo "Unable to find file '${2}'"
exit 1
fi
if [[ ! -r "${2}" ]]
then
echo "Unable to read file '${2}'"
exit 2
fi
gzip ${2}
ls -l ${2}.gz
这个脚本看起来很糟糕!(谢天谢地,它只是一个示例) 。这个脚本应该尽可能进行浓缩 。从便于阅读的角度来看,清单 2 提供的版本更干净 。
清单 2. 对清单 1 脚本进行浓缩的版本
#!/usr/bin/ksh
exit_msg() {
[[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
exit ${1:-0}
}
[[ $# -lt 2 ]] && exit_msg 0 "Usage: ${0##*/}
for _FNAME in $@
do
[[ ! -f "${_FNAME}" ]] && exit_msg 1 "Unable to find file '${_FNAME}'"
[[ ! -r "${_FNAME}" ]] && exit_msg 2 "Unable to read file '${_FNAME}'"
gzip ${_FNAME}
ls -l ${_FNAME}.gz
done
注意到这两者的差异了吗?这个脚本增加了一个简单的函数来显示一个消息并带适当的返回码退出,还把所有操作转移到一个 for 循环中,这使这个脚本看起来更干净、更容易理解了 。
保持灵活性
编程和 shell 脚本编程的新手常常犯的另一个错误是,在程序或 shell 脚本中对静态值进行硬编码 。这会限制脚本的灵活性,是一种糟糕的编程习惯 。这迫使管理员或开发人员不得不经常修改脚本以使用其他值;为了避免这个问题,应该使用变量并为脚本或函数提供参数 。
例如,清单 3 是一个编写得很差的不灵活的示例脚本 。
【对话 UNIX: 更多 shell 脚本技术】清单 3. 不灵活的示例脚本
#!/bin/bash
if [[ -f /home/cormany/FileA ]]
then
echo "Found file '/home/cormany/FileA'"
elif [[ -f /home/cormany/DirA/FileA ]]
then
echo "Found file '/home/cormany/DirA/FileA'"
else
echo "Unable to find file FileA"
fi
这个脚本可以正常工作,但是它只能在两个位置搜索一个文件 。
清单 4 提供相同的功能,但是允许用户在任何位置搜索任何文件 。
清单 4. 使脚本更灵活
#!/bin/bash
exit_msg() {
[[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
exit ${1:-0}
}
[[ $# -lt 2 ]] && exit_msg 1 "Usage: ${0##*/}
_FNAME="${1}"
_DNAME="${2}"
[[ ! -d "${_DNAME}" ]] && exit_msg 2 "Unable to read or find Directory '${_DNAME}'"
推荐阅读
- 对话 UNIX: !$#@*%
- UNIX 共享内存应用中的问题及解决方法
- 用Syslog 记录UNIX和Windows日志的方法
- 本周五Unix系统将发生时间错误
- 如何在Unix结果中抽取想要的参数
- 同步 UNIX 文件
- /dev/tty文件在Unix操作系统中的妙用
- 有关UNIX启动的一些体会
- NetWare与UNIX的互联方法与实现
- Unix系统下Find命令的三个高级应用