参考:cShell脚本模板_运维@小兵的博客-CSDN博客
#!/bin/bash
################################文件头############################
# Copyright 2022, xxxxxx Co. Ltd.
# All rights reserved.
# FileName: case001.sh
# Description: first case for test.
# Author: Michael
# http://www.xxxxxx.com
# Revision: 1.0.0
#################################################################
set -e #打开异常退出功能
# set -x #打开Debug功能
######################定义变量######################
source /etc/profile #避免用contab、ansible、Jenkins执行shell脚本出现环境变量不对的问题
WORKDIR="$(cd $(dirname "$0") || exit 1;pwd)" #脚本所在路径
echo "Current Excute: bash ${WORKDIR}/$0 $@"
function_dir=${WORKDIR}/myfunction.lib
#创建日志文件
if [[ ! -d ${WORKDIR}/logs ]];then
mkdir -p ${WORKDIR}/logs
else
rm -f ${WORKDIR}/logs/*.log
fi
[[ $UID -ne 0 ]] && echo "[ERROR] Please Use root Excute......" && exit 1
#输出信息
# ${FUNCNAME[0]代表当前函数名,$LINENO代表当前代码行号
echo "$(date "+%Y-%m-%d %T.%N")[ERROR ${FUNCNAME[0]}:$LINENO] Invalid Param"
echo "$(date "+%Y-%m-%d %T.%N")[INFO]:Install Success"
#加载函数库
if [[ -f "$function_dir" ]];then
source $function_dir
else
echo -e "\033[31m函数库不存在\033[0m"
exit 71
fi
######################功能函数######################
#检查环境
Check_Env() {
echo "[INFO] Begin Check_Env..."
[[ $UID -ne 0 ]] && echo "[ERROR] Please Use Admin(root) Excute......" && exit 1
#检查命令是否存在
for cmd_bin in curl mvn
do
if ! command -v ${cmd_bin} &> /dev/null;then
echo "[ERROR] ${cmd_bin} command Not Exist" && exit 1
fi
done
echo "[INFO] Check_Env Success"
}
#帮助信息
Help() {
cat << EOF
Usage:
=======================================================================
optional arguments:
-h 提供帮助信息
-num 虚拟机编号
EXAMPLE:
bash $0 -num 10 web1 eth0 192.168.4.1/24
EOF
}
#######################主函数#######################
#参数校验
[[ $# -ne 1 ]] && echo "[ERROR] Invalid Param!!! eg:bash $0 ansible_path" && exit 1
[[ $# -le 5 ]] && echo "[ERROR] Invalid Param!!!,Please Excute:bash $0 -h" && exit 1
#主函数
main() {
Print
cecho 32 "开始执行......"
echo "1.本地源"
echo "2.网络源"
read -p "请选择:" choice
case ${choice} in
1)
Conf_Apt;;
2)
echo "1.ubuntu14.04"
echo "2.ubuntu16.04"
echo "3.ubuntu18.04"
read -p "请选择系统版本:" choice
case ${choice} in
1)
Conf_Ubuntu14.04;;
2)
Conf_Ubuntu16.04;;
3)
Conf_Ubuntu18.04;;
*)
cecho 31 "Invalid option!"
esac
;;
*)
cecho 31 "Invalid option!"
esac
}
main
if [[ $# -eq 0 ]];then
Excute_All
elif [[ "$1" == "-c" -a "$#" -eq 2 ]];then
case $2 in
system)
Init_System;;
*)
cecho 31 "Invalid option:bash `basename $0` [-h]"
esac
elif [[ "$1" == "-h" ]];then
Help
else
Help && exit 1
fi
#读取短参数
[[ $# -eq 0 ]] && HELP
while getopts :hnum::a: ARGS
do
case $ARGS in
h)
HELP;;
nu|m)
Name=rh7_node$OPTARG;;
\?)
cecho 31 "Invalid option:bash `basename $0` [-h]"
esac
done
使用getopts进行参数解析
while getopts "b:o:h" opt_name
do
case $opt_name in
b) echo "-b Option is recognized, argument=$OPTARG"
build
;;
o) echo "-o Option is recognized, argument=$OPTARG"
update $OPTARG
;;
h) echo "-h Option is recognized"
package
;;
:) echo "$OPTARG Option need a argument" # 比如输出-b 但是又没有指定参数,就走到这里
print_help
;;
esac
done
function build()
{
echo "building ..."
}
build
function update()
{
update_mode=$1
# “STRING” 的长度为零则为真
if [ -z "${update_mode}" ]
then
echo ">> ERROR: build mode required."
return 1
fi
if [[ ${update_mode} != "release" ]] && [[ ${update_mode} != "debug" ]]
then
echo ">> ERROR: invalid build mode: ${update_mode}"
return 1
fi
if [ ${update_mode} == "release" ]
then
echo ">>>> relese update mode ..."
fi
if [ ${update_mode} == "debug" ]
then
echo ">>>> debug update mode ..."
fi
return 0
}
update debug
function build_svr() {
core=$(grep -c ^processor /proc/cpuinfo)
echo ${core}
res=$(make svr)
if [ $? != 0 ]
then
echo "build svr fail"
fi
echo "build svr successfully"
echo ${res}
}
function update() {
update_mode=$1
# # “STRING” 的长度为零则为真
if [ -z "${update_mode}" ]
then
echo ">> ERROR: build mode required."
return 1
fi
if [[ ${update_mode} != "release" ]] && [[ ${update_mode} != "debug" ]]
then
echo ">> ERROR: invalid build mode: ${update_mode}"
return 1
fi
if [ ${update_mode} == "release" ]
then
echo ">>>> relese update mode ..."
fi
if [ ${update_mode} == "debug" ]
then
echo ">>>> debug update mode ..."
fi
return 0
}
SCRIPT_DIR="$(cd `dirname $0`; pwd)"
SCRIPT_NAME=$(basename $0)
OUTPUT_PATH=${SCRIPT_DIR}/data
function show_path() {
echo ${SCRIPT_DIR}
echo ${SCRIPT_NAME}
cd ${OUTPUT_PATH}
echo "hello" > 1.log
cd ${SCRIPT_DIR}
}
show_path
function svn_checkout() {
if [ ! -d ${OUTPUT_PATH} ]
then
mkdir ${OUTPUT_PATH}
echo "create dir"
fi
echo "dir created"
rm -rf ${OUTPUT_PATH}
cd ${OUTPUT_PATH}
svn checkout ${SVN_URL} . --username ${USERNAME} --password ${PASSWORD}
cd ${SCRIPT_DIR}
}
SCRIPT_DIR="$(cd `dirname $0`; pwd)"
SCRIPT_NAME=`basename $0`
OUTPUT_PATH=${SCRIPT_DIR}/data
SHELL_CFG=${SCRIPT_DIR}/cfg.ini
function load_cfg() {
if [ -f "${SHELL_CFG}" ]
then
source ${SHELL_CFG}
fi
echo ${OUTPUT_CFG_PATH}
}
load_cfg
echo ${OUTPUT_CFG_PATH}
对应的ini文件(cfg.ini)
OUTPUT_CFG_PATH=./data
print_date() {
echo "========date========"
date
}
LOG_FILE="build.log"
ERR_FILE="build.err"
TEE="/usr/bin/tee"
set_env() {
if [ ${SILENT} == 'true' ]
then
exec 1>${LOG_FILE}
exec 2>${ERR_FILE}
else
NPIPE=/tmp/$$.tmp
trap "rm -f ${NPIPE}" EXIT
mknod $NPIPE p
${TEE} <$NPIPE ${LOG_FILE} &
exec 1>&- # &- 关闭标准输出 # n&- 表示将 n 号输出关闭
exec 1>$NPIPE
exec 2>${ERR_FILE}
fi
}
脚本debug神器,把每一行的脚本执行了什么都打印出来
#!/bin/sh -x
作者:DayDayUpppppp
链接:https://www.jianshu.com/p/34c76024c0ef
修改文件 /etc/vimrc
追加到末尾就好
set ignorecase
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1, "#!/bin/bash")
call setline(2,"##############################################################")
call setline(3, "# File Name:".expand("%"))
call setline(4, "# Version:V1.0")
call setline(5, "# Author:quanheng")
call setline(6, "# Organization:www.quanheng77.top")
call setline(7, "# Desc:")
call setline(8,"##############################################################")
endif
endfunc
(摘自:Shell编程之代码规范_shell脚本指定编码格式)
2.1. 文件头
#!/bin/bash
################################################################
# Copyright 2022, xxxxxx Co. Ltd.
# All rights reserved.
# FileName: case001.sh
# Description: first case for test.
# Author: Michael
# http://www.xxxxxx.com
# Revision: 1.0.0
#################################################################
# Delete a file in a sophisticated manner.
#######################################
# Get configuration directory.
# Globals:
# SOMEDIR
# Arguments:
# None
# Outputs:
# Writes location to stdout
#######################################
get_dir() {
echo "${SOMEDIR}"
}
#######################################
# Delete a file in a sophisticated manner.
# Arguments:
# $1: File to delete, a path.
# Returns:
# 0 if thing was deleted, otherwise non-zero.
#######################################
del_thing() {
rm "$1"
}
tab键设置为4个空格,默认缩进为4个空格。
main() {
# 缩进4个空格
say="hello World."
echo "${say}"
}
function定义,默认不需要加function修饰。函数统一放在源文件的全局变量之后,可执行代码之前,函数之间不放置可执行代码。代码功能比较少时,可以不定义main函数。
main() {
echo "hello World."
exit 0
}
代码一行的最大长度限定在120个字符左右。
让; 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 (( $? != 0 )); then
error_message
fi
else
mkdir -p "${dir}/${ORACLE_SID}"
if (( $? != 0 )); then
error_message
fi
fi
done
可选项中的多个命令应该被拆分成多行,模式表达式、操作和结束符 ;; 在不同的行。
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
ysUtil::is_boot(){
return 1
}
get_path() {
echo "/dev/nvme0"
}
readonly MAX_PATH_LEN=256
test_dir() {
local path_name
path_name="$(get_path)" || return 1
if [ ${#path_name} -gt $MAX_PATH_LEN ]; then
return 0
fi
return 1
}
# Special variables
echo $1 $2 $3
echo $? $!
# 当位置变量大于等于10,则必须有大括号:
echo "many parameters: ${10}"
# 当出现歧义时,必须有大括号:
# Output is "a0b0c0"
set -- a b c
echo "${1}0${2}0${3}0"
# 使用变量扩展赋值时,必须有大括号:
DEFAULT_MEM=${DEFUALT_MEM:-"-Xms2g -Xmx2g -XX:MaxDirectMemorySize=4g"}
# 其他常规变量的推荐处理方式:
echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
while read f; do
echo "file=${f}"
done < <(ls -l /tmp)
引用通常情况下应遵循以下原则:
● 默认情况下推荐使用引号引用包含变量、命令替换符、空格或shell元字符的字符串
● 在有明确要求必须使用无引号扩展的情况下,可不用引号
● 字符串为单词类型时才推荐用引号,而非命令选项或者路径名
● 不要对整数使用引号
● 特别注意 [[ 中模式匹配的引号规则
● 在无特殊情况下,推荐使用 $@ 而非 $*
# '单引号' 表示禁用变量替换
# "双引号" 表示需要变量替换
# 1: 命令替换需使用双引号
flag="$(some_command and its args "$@" 'quoted separately')"
# 2:常规变量需使用双引号
echo "${flag}"
# 3:整数不使用引号
value=32
# 示例4:即便命令替换输出为整数,也需要使用引号
number="$(generate_number)"
echo "$value"
# 5:单词可以使用引号,但不作强制要求
readonly USE_INTEGER='true'
# 6:输出特殊符号使用单引号或转义
echo 'Hello stranger, and well met. Earn lots of $$$'
echo "Process $$: Done making \$\$\$."
# 7:命令参数及路径不需要引号
grep -li Hugo /dev/null "$1"
# 8:常规变量用双引号,ccs可能为空的特殊情况可不用引号
git send-email --to "${reviewers}" ${ccs:+"--cc" "${ccs}"}
# 9:正则用单引号,$1可能为空的特殊情况可不用引号
grep -cP '([Ss]pecial|\|?characters*)$' ${1:+"$1"}
# 10:位置参数传递推荐带引号的"$@",所有参数作为单字符串传递用带引号的"$*"
# content of t.sh
func_t() {
echo num: $#
echo args: 1:$1 2:$2 3:$3
}
func_t "$@"
func_t "$*"
# 当执行 ./t.sh a b c 时输出如下:
num: 3
args: 1:a 2:b 3:c
num: 1
args: 1:a b c 2: 3:
(摘自:https://blog.csdn.net/feihe0755/article/details/126484099)
3.4. Error输出到STDERR
所有的错误信息都应该输出到STDERR。
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if ! do_something; then
err "Unable to do_something"
exit 1
fi
使用新式语法$(command),不使用老式语法反引号,新语法可读性更高。
# good
var=$(cat name.txt|grep "100")
# bad
var=`cat name.txt|grep "100"`
优先使用[[ … ]],而不是[ … ], test,因为在 [[ 和 ]] 之间不会出现路径扩展或单词切分,所以使用 [[ … ]] 能够减少犯错,且 [[ … ]] 支持正则表达式匹配,而 [ … ] 不支持。
载入外部文件推荐使用source,代码可读性更好。
# 推荐
source base.sh
# 不推荐
. base.sh
非必要情况,不使用管道传递参数,直接使用参数,效率更高。
# 推荐
grep "main" main.cpp
wc -l log.config
# 不推荐
cat main.cpp | grep "main"
cat log.config | wc -l
简单的数学计算可以使用(()),复杂的计算使用awk或bc。
# 推荐
(( i = 10 * j + 400 ))
# 可行,但是不推荐
i=$( expr 4 + 4 )
需要检查命令返回值
if ! mv "${file_list[@]}" "${dest_dir}/"; then
echo "Unable to move ${file_list[*]} to ${dest_dir}" >&2
exit 1
fi
# Or
mv "${file_list[@]}" "${dest_dir}/"
if (( $? != 0 )); then
echo "Unable to move ${file_list[*]} to ${dest_dir}" >&2
exit 1
fi
有一些命令,即支持外部命令工具,也支持Shell自带语法,更推荐使用自带内部命令,效率更高。
# 推荐使用内建的算术扩展
addition=$((${X} + ${Y}))
# 推荐使用内建的字符串替换
substitution="${string/#foo/bar}"
# 不推荐调用外部命令进行简单的计算
addition="$(expr ${X} + ${Y})"
# 不推荐调用外部命令进行简单的字符串替换
substitution="$(echo "${string}" | sed -e 's/^foo/bar/')"