函数名
小写字母、下划线:
# Single function
my_func() {
...
}
# Part of a package
mypackage::my_func() {
...
}
变量名
小写字母、下划线、循环变量:
for zone in ${zones}; do
something_with "${zone}"
done
只读变量
使用readonly或者declare -r声明:
zip_version="$(dpkg --status zip | grep Version: | cut -d ‘ ‘ -f 2)"
if [[ -z "${zip_version}" ]]; then
error_message
else
readonly zip_version
fi
常量和环境变量
大写字母:
# Constant
readonly PATH_TO_FILES=‘/some/path‘
# Both constant and environment
declare -xr ORACLE_SID=‘PROD‘
VERBOSE=‘false‘
while getopts ‘v‘ flag; do
case "${flag}" in
v) VERBOSE=‘true‘ ;;
esac
done
readonly VERBOSE
使用本地变量
local声明:
my_func2() {
local name="$1"
# Separate lines for declaration and assignment:
local my_var
my_var="$(my_func)" || return
# DO NOT do this: $? contains the exit code of ‘local‘, not my_func
local my_var="$(my_func)"
[[ $? -eq 0 ]] || return
...
}
源文件名
小写字母、下划线:
make_template.sh
打印错误信息
err() {
echo "[$(date +‘%Y-%m-%dT%H:%M:%S%z‘)]: $@" >&2
}
if ! do_something; then
err "Unable to do_something"
exit "${E_DID_NOTHING}"
fi
基本语句格式
缩进四个空格,或者一个制表符,一个制表符设置为四个空格。
行的长度最大为80字符,使用\换行。
管道:如果一行容得下整个管道操作,那么请将整个管道操作写在同一行。否则,应该将整个管道操作分割成每行一个管段,管道操作的下一部分应该将管道符放在新行并且缩进2个空格。这适用于使用管道符’|’的合并命令链以及使用’||’和’&&’的逻辑运算链。
# All fits on one line
command1 | command2
# Long commands
command1 | command2 | command3 | command4
for循环
请将?;?do?,?;?then?和?while?,?for?,?if?放在同一行。
for dir in ${dirs_to_cleanup}; do
if [[ -d "${dir}/${ORACLE_SID}" ]]; then
log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
rm "${dir}/${ORACLE_SID}/"*
if [[ "$?" -ne 0 ]]; then
error_message
fi
else
mkdir -p "${dir}/${ORACLE_SID}"
if [[ "$?" -ne 0 ]]; then
error_message
fi
fi
done
case语句
case "${expression}" in
a)
variable="..."
some_command "${variable}" "${other_expr}" ...
;;
absolute)
actions="relative"
another_command "${actions}" "${other_expr}" ...
;;
*)
error "Unexpected expression ‘${expression}‘"
;;
esac
verbose=‘false‘
aflag=‘‘
bflag=‘‘
files=‘‘
while getopts ‘abf:v‘ flag; do
case "${flag}" in
a) aflag=‘true‘ ;;
b) bflag=‘true‘ ;;
f) files="${OPTARG}" ;;
v) verbose=‘true‘ ;;
*) error "Unexpected option ${flag}" ;;
esac
done
变量扩展
用?\({var}?而不是?\)var?,详细解释如下:
- 与现存代码中你所发现的保持一致。
- 引用变量参阅下面一节,引用。
- 除非绝对必要或者为了避免深深的困惑,否则不要用大括号将单个字符的shell特殊变量或定位变量括起来。推荐将其他所有变量用大括号括起来。
引用
set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$*"; echo "$#, $@"
set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$@"; echo "$#, $@"
命令替换
使用?$(command)?而不是反引号`command`。
test、[和[[
推荐使用?[[?...?]]?,而不是?[?,?test?, 和?/usr/bin/?[?。
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
测试字符串
if [[ "${my_var}" = "some_string" ]]; then
do_something
fi
if [[ -n "${my_var}" ]]; then
do_something
fi
# string不空时为真
if [[ -z "${my_var}" ]]; then
do_something
fi
# string空时为真
if [[ "${my_var}" = "" ]]; then
do_something
fi
# string空时为真
文件名的通配符扩展
因为文件名可能以?-?开头,所以使用扩展通配符?./*?比?*?来得安全得多。
rm -v ./* # 好
rm -v * # 不好
管道导向while循环
管道导向while循环中的隐式子shell使得追踪bug变得很困难。
last_line=‘NULL‘
your_command | while read line; do
last_line="${line}"
done
echo "${last_line}"
如果你确定输入中不包含空格或者特殊符号(通常意味着不是用户输入的),那么可以使用一个for循环。
total=0
for value in $(command); do
total+="${value}"
done
使用过程替换允许重定向输出,但是请将命令放入一个显式的子shell中,而不是bash为while循环创建的隐式子shell。
total=0
last_file=
while read count filename; do
total+="${count}"
last_file="${filename}"
done < <(your_command | uniq -c)
echo "Total = ${total}"
echo "Last one = ${last_file}"
当不需要传递复杂的结果给父shell时可以使用while循环。这通常需要一些更复杂的“解析”。请注意简单的例子使用如awk这类工具可能更容易完成。当你特别不希望改变父shell的范围变量时这可能也是有用的。
cat /proc/mounts | while read src dest type opts rest; do
if [[ ${type} == "nfs" ]]; then
echo "NFS ${dest} maps to ${src}"
fi
done
检查返回值
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; then
echo "Unable to move ${file_list} to ${dest_dir}" >&2
exit "${E_BAD_MOVE}"
fi