需求:
sleep 作为daemon进程运行,可以以配置文件的方式指定睡眠时长。 以及要实现切换mode start/kill 。
脚本实现思路
先使用getopt实现对参数以及选项的解析。(-- 后面解析的是非选项型参数)
对解析到的参数进行判断是否合法?
例如配置文件是否为空?
参数是否可以一起使用?
将解析到的参数保存在变量中以便在后续的功能中使用。
给默认值,像ls,如果不写选项,肯定会有默认选项的
sleep的默认就是start模式,start模式必须跟一个时间或者配置文件,否则应该提示用户输入时间。
sleep默认的pid输出文件为/tmp/mysleep.pid
再实现脚本的具体功能、例如start、kill..
要点:
有的选项是需要有默认值的
有的选项不能同时出现,例如-s 和 -k不能同时出现。sleep时长的配置指定时间或者配置文件即可。
详细的注释已在脚本中,欢迎大家点赞拍砖。
#!/usr/bin/bash # author: ninesun # date: 2022年1月4日22:27:23 VERSION=1.0 usage(){ cat <<'EOF' Usage: $0 [options] [TIME] options: -s,--start start -k,--kill kill -p,--pidfile pidfile -c,--config config --help help --version version EOF } pstreeStatus(){ pstree -p | grep sleep } # ------------------------------------- getopt对选项和参数的处理START-------------------- # -o 解析短选项 skhvp:c:,p:c:中的冒号代表是参数型选项,其后一定要跟参数,例如 -p "123" -c "456" # -l 解析长选项 start,kill,pidfile:,config:,help,version. 选项之间逗号隔开,冒号含义和-o选项一致. # -- 之后都为参数,$@ 取的当前脚本所传进去的参数列表。作为getopt的parameter。 getopt按照 -o -l的格式进行解析。 # 解析之后的参数列表赋给parameters parameters=`getopt -o skhvpt:c: -l start,kill,pidfile:,config:,help,version,status -- "$@"` [ $? -ne 0 ] && exit 1 # getopt 如果解析失败,退出脚本. eval set -- "$parameters" # set 的--选项代表将命令行参数转换为位置参数$1 $2.. 以便后续脚本使用方便 while true;do case "$1" in #使用shift命令将参数 一直往左移动,使case一直处理第一个位置的参数 -p|--pidfile) pidfile=$2; shift 2;; #-p指定pidfile存放的位置. 对于选项型参数,选型和参数两个位置,因此要shift 2 -s|--start) [ -z "$mode"] && mode="start" || exit 1; shift;; # -z 判断mode是否为空,为空则默认为start。 -k|--kill) [ -z "$mode"] && mode="kill" || echo "-s and -k can't use together..."; exit 1; shift;; # -s和-k不能一起使用 -c|--config) config=$2; shift 2;; # sleep 时间的配置文件 -t|--status) pstreeStatus;exit ;; -h|--help) usage;exit ;; -v|--version) echo $VERSION;exit;; --) #-- 代表解析到非选项型参数,即 睡眠时间。 时间可以放在任何位置. shift # 向左移动一个位置,将--移出,是第一个参数为时间 time=$1 # ,shift之后, $1为时间参数 break # 已经解析到getopt格式化之后的最后一个参数了。因此break 直接跳出while循环. ;; *) usage;exit 4 # ;; esac done # 如果没有给任何选项,必须默认给一个操作模式 [ -z "$mode" ] && mode="start" # 默认是start # pid文件,默认为/tmp/mysleep.pid [ -z "$pidfile" ] && pidfile=/tmp/mysleep.pid # ------------------------------------- 对sleep 时间参数的处理 START--------------------- # -s模式下config和TIME给且仅给一个,应该判断同时存在,是否存在配置?、是否存在参数? 三种情况 #-k模式下可以不需要$time case "$mode" in start) if [ -n "$config" -a -n "$time" ];then # [ -n "$config" ]config 非空(非0),返回0(true)。以-a为分隔 ,左边和右边进行逻辑与。 相与为真代表传了两个时间参数. echo "-c(--config) and TIME can't use together..." exit 1 elif [ -n "$config" ];then #config 如果非空,read 读取,read if [ -r "$config" ];then #配置文件是否可读 read line <$config [ -z "$line" ] && exit 1 #"${line//[0-9]/}"替换line变量中的数字为空,正常情况应该为空,如果非空代表pid包含非数字. [ -n "${line//[0-9]/}" ] && { echo "config file must contain num...";exit 1; } time=$line else echo "config unreadable" exit 1 fi elif [ -n "$time" ];then [ -n "${time//[0-9]/}" ] && { echo "time must contain num...";exit 1; } else echo "give me a time to sleep..." exit 1 fi ;; kill) ;; *) usage;exit 1 esac echo mode: $mode echo time: $time echo pidfile: $pidfile # ------------------------------------- 实际业务主逻辑Start------------------------------- # 启动进程 startmysleep(){ pidfile=$1 time=$2 ( sleep $time & echo $! ) >>"${pidfile}" # 小括号会在一个子shell中执行. 所以echo $!打印当前sleep执行的pid要放在小括号里面。 } # 杀sleep:找到pid文件,然后杀掉进程 killmysleep(){ pidfile=$1 if [ -r "${pidfile}" ];then while read pid;do [ -z "$pid" ] && exit 1 # 如果为pid空则退出 # -n "${pid//[0-9]/}" 检查pid是否合法 #两个斜杠//代表贪婪替换。替换pid张红的数字,pid一定是只有数字的,如果pid中含有字母或空,则替换之后肯定为空,-n 如果不为空代表pid中有非法字符则退出. [ -n "${pid//[0-9]/}" ] && exit 1 kill $pid && rm -rf ${pidfile} done <${pidfile} return 0 fi return 1 } case "$mode" in start) startmysleep $pidfile $time ;; kill) killmysleep $pidfile ;; *) usage esac