如何写一个类似于ls的命令?(shell进阶)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 如何写一个类似于ls的命令?(shell进阶)

需求:

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
目录
相关文章
|
1月前
|
人工智能 Shell iOS开发
AI Shell:在命令行里“对话” AI ,微软推出将 AI 助手引入命令行的 CLI 工具,打造对话式交互命令行
AI Shell 是一款强大的 CLI 工具,将人工智能直接集成到命令行中,帮助用户提高生产力。AI Shell 支持多种 AI 模型和助手,通过多代理框架提供丰富的功能和灵活的使用模式。
139 7
|
1月前
|
Java Shell Windows
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
46 1
|
2月前
|
Web App开发 网络协议 Linux
linux命令总结(centos):shell常用命令汇总,平时用不到,用到就懵逼忘了,于是专门写了这篇论文,【便持续更新】
这篇文章是关于Linux命令的总结,涵盖了从基础操作到网络配置等多个方面的命令及其使用方法。
80 1
linux命令总结(centos):shell常用命令汇总,平时用不到,用到就懵逼忘了,于是专门写了这篇论文,【便持续更新】
|
2月前
|
Shell 知识图谱
Shell printf 命令
10月更文挑战第3天
26 1
|
2月前
|
Unix Shell Linux
常见的shell命令
shell常用命令
61 11
|
3月前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
3月前
|
Java Shell Windows
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
157 5
|
2月前
|
Shell PHP
Shell echo命令
10月更文挑战第3天
25 0
|
2月前
|
JSON Java Shell
Dockerfile中RUN、CMD、ENTRYPOINT、SHELL命令的区别
理解这些指令的差异和应用场景,有助于构建高效、灵活且易于管理的Docker镜像。在实际应用中,根据需要选择合适的指令,可以有效地控制镜像构建和容器运行的行为。
241 0
|
2月前
|
SQL Shell 数据库
在TDengine容器中创建初始化数据库的Shell命令实例
以上就是在Docker容器环境中部署并初始化TDengine数据库的全过程,希望对你有所帮助。
95 0