个人网站要怎么备案有网站模板怎么建站
什么是shell脚本
当命令或者程序语句写在文件中,我们执行文件,读取其中的代码,这个程序就称之为shell脚本。
有了shell脚本肯定是要有对应的解释器了,常见的shell脚本解释器有sh、python、perl、tcl、php、ruby等。一般这种使用文件方式来执行sh命令的方式被称为非交互方式。
- Windows中存在的*.bat批处理脚本。
- Linux中常用*.sh脚本文件。
shell脚本规则
在Linux系统中,shell脚本或者称之为bash shell程序,通常都是vim编辑,有Linux命令、bash shell指令、逻辑控制语句和注释信息组成。
google bash编程规范链接
- 1.使用.sh这样特定语言后缀作为扩展名,可以快速识别文件。
- 2.set可以设置shell的选项。SUID(Set User ID)和SGID(Set Group ID)在shell脚本中是被禁止的,如果你需要较高权限的访问请使用
sudo
。 - 3.建议使用
STDERR
打印错误信息。
err() {echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2
}if ! do_something; thenerr "Unable to do_something"exit "${E_DID_NOTHING}"
fi
- 4.文件头:指定脚本解释器,这里是用#!/bin/bash做解释器的。
#!/bin/bash
#
# Perform hot backups of Oracle databases.
- 5.函数注释应该包括:
- 函数的描述
- 全局变量的使用和修改
- 使用的参数说明
- 返回值,而不是上一条命令运行后默认的退出状态
例如:
#!/bin/bash
#
# Perform hot backups of Oracle databases.export PATH='/usr/xpg4/bin:/usr/bin:/opt/csw/bin:/opt/goog/bin'#######################################
# Cleanup files from the backup dir
# Globals:
# BACKUP_DIR
# ORACLE_SID
# Arguments:
# None
# Returns:
# None
#######################################
cleanup() {...
}
- 6.TODO注释应该包含全部大写的字符串TODO,接着是括号中你的用户名。冒号是可选的。最好在TODO条目之后加上 bug或者ticket 的序号。
# TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
- 7.缩进一般使用两个空格,没有制表符。
- 8.行的长度和长字符串最大长度为
80
个字符。
使用下述两种方式规避长字符串:
# DO use 'here document's
cat <<END;
I am an exceptionally long
string.
END# Embedded newlines are ok too
long_string="I am an exceptionallylong string."
- 9.管道尽量写在一行,否则应该将整个管道操作分割成每行一个管段,管道操作的下一部分应该将管道符放在新行并且缩进
2
个空格。这适用于使用管道符’|’的合并命令链以及使用’||’和’&&’的逻辑运算链。
# All fits on one line
command1 | command2# Long commands
command1 \| command2 \| command3 \| command4
- 10.循环中,
; do
,; then
应该和if/for/while
放在同一行。else
应该单独一行,结束语句应该单独一行并且跟开始语句垂直对齐。
例如:
for dir in ${dirs_to_cleanup}; doif [[ -d "${dir}/${ORACLE_SID}" ]]; thenlog_date "Cleaning up old files in ${dir}/${ORACLE_SID}"rm "${dir}/${ORACLE_SID}/"*if [[ "$?" -ne 0 ]]; thenerror_messagefielsemkdir -p "${dir}/${ORACLE_SID}"if [[ "$?" -ne 0 ]]; thenerror_messagefifi
done
- 11.case语句模式表达式前面不应该出现左括号。避免使用
;&
和;;&
符号。关键规则如下:
* 通过2
个空格缩进可选项。
* 在同一行可选项的模式右圆括号之后和结束符;;
之前各需要一个空格。
* 长可选项或者多命令可选项应该被拆分成多行,模式、操作和结束符;;
在不同的行。
例子1:
case "${expression}" ina)variable="..."some_command "${variable}" "${other_expr}" ...;;absolute)actions="relative"another_command "${actions}" "${other_expr}" ...;;*)error "Unexpected expression '${expression}'";;
esac
例子2:
verbose='false'
aflag=''
bflag=''
files=''
while getopts 'abf:v' flag; docase "${flag}" ina) aflag='true' ;;b) bflag='true' ;;f) files="${OPTARG}" ;;v) verbose='true' ;;*) error "Unexpected option ${flag}" ;;esac
done
- 12.变量扩展应该保持跟你所发现的一致,引用你的变量,推荐用
${var}
而不是$var
例如:
# Section of recommended cases.# Preferred style for 'special' variables:
echo "Positional: $1" "$5" "$3"
echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ ..."# Braces necessary:
echo "many parameters: ${10}"# Braces avoiding confusion:
# Output is "a0b0c0"
set -- a b c
echo "${1}0${2}0${3}0"# Preferred style for other variables:
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
while read f; doecho "file=${f}"
done < <(ls -l /tmp)# Section of discouraged cases# Unquoted vars, unbraced vars, brace-quoted single letter
# shell specials.
echo a=$avar "b=$bvar" "PID=${$}" "${1}"# Confusing use: this is expanded as "${1}0${2}0${3}0",
# not "${10}${20}${30}
set -- a b c
echo "$10$20$30"
- 13.引用
- 除非需要小心不带引用的扩展,否则总是引用包含变量、命令替换符、空格或shell元字符的字符串
- 推荐引用是单词的字符串(而不是命令选项或者路径名)
- 千万不要引用整数
- 注意
[[
中模式匹配的引用规则 - 请使用
$@
除非你有特殊原因需要使用$*
例如:
# 'Single' quotes indicate that no substitution is desired.
# "Double" quotes indicate that substitution is required/tolerated.# Simple examples
# "quote command substitutions"
flag="$(some_command and its args "$@" 'quoted separately')"# "quote variables"
echo "${flag}"# "never quote literal integers"
value=32
# "quote command substitutions", even when you expect integers
number="$(generate_number)"# "prefer quoting words", not compulsory
readonly USE_INTEGER='true'# "quote shell meta characters"
echo 'Hello stranger, and well met. Earn lots of $$$'
echo "Process $$: Done making \$\$\$."# "command options or path names"
# ($1 is assumed to contain a value here)
grep -li Hugo /dev/null "$1"# Less simple examples
# "quote variables, unless proven false": ccs might be empty
git send-email --to "${reviewers}" ${ccs:+"--cc" "${ccs}"}# Positional parameter precautions: $1 might be unset
# Single quotes leave regex as-is.
grep -cP '([Ss]pecial|\|?characters*)$' ${1:+"$1"}# For passing on arguments,
# "$@" is right almost everytime, and
# $* is wrong almost everytime:
#
# * $* and $@ will split on spaces, clobbering up arguments
# that contain spaces and dropping empty strings;
# * "$@" will retain arguments as-is, so no args
# provided will result in no args being passed on;
# This is in most cases what you want to use for passing
# on arguments.
# * "$*" expands to one argument, with all args joined
# by (usually) spaces,
# so no args provided will result in one empty string
# being passed on.
# (Consult 'man bash' for the nit-grits ;-)set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$*"; echo "$#, $@")
set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$@"; echo "$#, $@")
- 14.命令替换应使用
$(command)
而不是反引号
# This is preferred:
var="$(command "$(command1)")"# This is not:
var="`command \`command1\``"
- 15.test, [和[[时,推荐使用
[[ ... ]]
,而不是[
,test
, 和/usr/bin/[
# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at http://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ [^[:digit:]]+name ]]; thenecho "Match"
fi# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; thenecho "Match"
fi# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; thenecho "Match"
fi
- 16.测试字符串尽可能使用引用,而不是过滤字符串
例子1
# Do this:
if [[ "${my_var}" = "some_string" ]]; thendo_something
fi# -z (string length is zero) and -n (string length is not zero) are
# preferred over testing for an empty string
if [[ -z "${my_var}" ]]; thendo_something
fi# This is OK (ensure quotes on the empty side), but not preferred:
if [[ "${my_var}" = "" ]]; thendo_something
fi# Not this:
if [[ "${my_var}X" = "some_stringX" ]]; thendo_something
fi
例子2
# Use this
if [[ -n "${my_var}" ]]; thendo_something
fi# Instead of this as errors can occur if ${my_var} expands to a test
# flag
if [[ "${my_var}" ]]; thendo_something
fi
- 17.文件名的通配符扩展,当进行文件名的通配符扩展时,请使用明确的路径
# Here's the contents of the directory:
# -f -r somedir somefile# This deletes almost everything in the directory by force
psa@bilby$ rm -v *
removed directory: `somedir'
removed `somefile'# As opposed to:
psa@bilby$ rm -v ./*
removed `./-f'
removed `./-r'
rm: cannot remove `./somedir': Is a directory
removed `./somefile'
- 18.Eval应该避免使用
eval简单例子
# What does this set?
# Did it succeed? In part or whole?
eval $(set_my_variables)# What happens if one of the returned values has a space in it?
variable="$(eval some_function)"
- 19.不建议管道导向while循环
- 20.函数名,使用小写字母,并用下划线分隔单词。使用双冒号 :: 分隔库。函数名之后必须有圆括号。关键词 function 是可选的,但必须在一个项目中保持一致
# Single function
my_func() {...
}# Part of a package
mypackage::my_func() {...
}
当函数名后存在 () 时,关键词 function 是多余的。但是其促进了函数的快速辨识。
- 21.变量名,如函数名命名相同,循环的变量名应该和循环的任何变量同样命名
for zone in ${zones}; dosomething_with "${zone}"
done
- 22.常量和环境变量名,全部大写,用下划线分隔,声明在文件的顶部
# Constant
readonly PATH_TO_FILES='/some/path'# Both constant and environment
declare -xr ORACLE_SID='PROD'
动态生成的常量,可以使用readonly
和 export
来进行设置,在函数中declare
不会对全局变量进行操作。
VERBOSE='false'
while getopts 'v' flag; docase "${flag}" inv) VERBOSE='true' ;;esac
done
readonly VERBOSE
- 23.源文件名使用小写,如果需要的话使用下划线分隔单词
- 24.只读变量使用
readonly
或者declare -r
来确保变量只读
zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
if [[ -z "${zip_version}" ]]; thenerror_message
elsereadonly zip_version
fi
- 25.本地变量使用
local
声明特定功能的变量。声明和赋值应该在不同行。local
作用域为函数内部和子函数中可见。
my_func2() {local name="$1"# Separate lines for declaration and assignment:local my_varmy_var="$(my_func)" || return# DO NOT do this: $? contains the exit code of 'local', not my_funclocal my_var="$(my_func)"[[ $? -eq 0 ]] || return...
}
- 26.函数位置:将文件中所有的函数一起放在常量下面。不要在函数之间隐藏可执行代码
- 27.主函数main:为了方便查找程序的开始,将主程序放入一个称为 main 的函数,作为最下面的函数
- 28.检查返回值:使用 $? 或直接通过一个 if 语句来检查以保持其简洁
例子1
if ! mv "${file_list}" "${dest_dir}/" ; thenecho "Unable to move ${file_list} to ${dest_dir}" >&2exit "${E_BAD_MOVE}"
fi# Or
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; thenecho "Unable to move ${file_list} to ${dest_dir}" >&2exit "${E_BAD_MOVE}"
fi
例子2(Bash内部PIPESTATUS
变量)
-作用
tar -cf - ./* | ( cd "${dir}" && tar -xf - )
if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; thenecho "Unable to tar files to ${dir}" >&2
fi
例子3(PIPESTATUS
优化)
tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
return_codes=(${PIPESTATUS[*]})
if [[ "${return_codes[0]}" -ne 0 ]]; thendo_something
fi
if [[ "${return_codes[1]}" -ne 0 ]]; thendo_something_else
fi
- 29.内建命令和外部命令选择上请使用内建命令
# Prefer this:
addition=$((${X} + ${Y}))
substitution="${string/#foo/bar}"# Instead of this:
addition="$(expr ${X} + ${Y})"
substitution="$(echo "${string}" | sed -e 's/^foo/bar/')"