zoukankan      html  css  js  c++  java
  • shell 选项解析之需求一:多路径自动补全

    前言

    上篇实现了shell选项的简单解析,支持选项连写(-xfd)及带参选项(-d “目的路径”)。但在使用中发现仍有不便之处:经常需要上传多个文件到设备下的不同路径,若脚本只支持单路径,则只能按路径分批上传;其次,有些文件名比较长,输入麻烦,so,新的需求产生了~

    1)支持对文件指定不同目的路径

    2)考虑到不少情况下只是覆盖原文件,若目的路径可带文件名,则能利用shell的路径补全简化文件名的输入,即路径选项支持:-d /usr/bin/file_with_a_very_long_name

    【补630】唉,人都是有惰性的,若功能顺应了惰性,则惰性会打蛇随棍上,反向推动功能演化,这从侧面反映了科技与欲望的关系,所以西方科技曾被视为奇技淫巧是有文化根据的,呵呵。 言归正传,使用中发现,若支持文件名补全,则对于长点的文件名,都会有使用特性 2)的心理倾向,导致选项经常像这样 -d /usr/bin/file1, -d /usr/bin/file2, -d /opt/file3,不停的输入 -d 很麻烦,因为输入时需寻找“-”在键盘上的位置,从人体工学的角度来说,符号“-”的I/O时间远大于其它普通字符,导致输入流水线断流,违背了GTD中及时清空大脑的要求,叔婶均无可忍之。这样吧,若参数里带“/”,则认为是目的路径,前面带不带选项“-d”都行

    思考

    1、可以修改原脚本,在流程处理中添加对新需求的支持。但这样做会得不偿失,因为原脚本功能单一,遵循标准的选项处理方式。直接修改会破坏模块封装,引起功能紊乱,导致难以复用。若标准化封装模块是块平直的砖,则功能添加版就是块更大的砖,但不甚平直,前者可建高楼,后者就只能盖屋,太高就 Hold 不住撒~

    2、既然不改原模块,则考虑在原功能基础上实现。一个自然的想法是使新脚本支持批量选项,即 原选项1 分隔符 原选项2 分隔符 ...,这样的话,新脚本只需根据分隔符把各选项分割出来,然后传给标准模块处理即可,如, -xd /usr/bin snmp 分隔符 -d /opt/local webs ...,从而支持了多目的路径

    3、对文件名补全的支持:与 2 类似,也要转为选项的标准形式来处理,即把 -d /usr/bin/snmp 转为 -d /usr/bin snmp,把粘在一块的扯明白喽~

    4、其它考虑:多数情况下,都是从一台主机上传文件,所以约定,若最后一个参数为ip,则此ip作为所有选项的默认主机地址;

    目标

    支持多目的路径;支持带文件名路径

    例: -xfd /usr/bin/linux-2.6.13-mips-snmpv2 webs分隔符 -d /opt/local wtp 分隔符...  .68

    解释:把文件linux-2.6.13-mips-snmpv2,webs从主机192.168.0.68上传到设备的/usr/bin目录下,并后台执行;把文件wtp从192.168.0.68上传到/opt/local下...

    【补630】不要 -d 也支持,-xf /usr/bin/linux-2.6.13-mips-snmpv2 webs 分隔符 /opt/local wtp

    实现

    1. 分隔符的选择

    感觉标准点的,如 --add,但字符多了点; 分号“;”?不可,shell会试图解释,先用逗号顶上吧

    2. 如何分割选项

    直观的方法是依次把两个连续分隔符间的内容取出,标准化处理后作为参数传出。shell的字符串处理功能不弱,看看有哪些趁手的工具先~

    expr 命令,length求长度,index得索引,substr截取字符串,match根据正则表达式获得匹配串的长度和内容

          index + substr 方式,通过index 得到两个连续分隔符的位置,substr根据位置截取中间,但index只能匹配到第一个字符

          match,写个分隔符的正则,把之间的内容取出,但试了下,似乎是贪婪的方式哦,会取最大匹配

    忽然发现以上思路有偏差,依赖直观而没脚踏实地,从机器扫描的方式来看,从左至右,碰到一个分隔符,就该认为一个项已取完,可以送出了,然后删掉此分隔符,继续扫描。

    思路明确了,这里用子串削除来取得一项,${参数列表%%分隔符*},即右边取最大匹配,削掉丫的;至于格式的标准化,用for来选择处理

    3. 代码

     1 tftp_multi_da()
     2 {
     3    ## 1. 先处理默认主机 IP
     4    local args_list="$*"
     5    eval local last_arg=\"\${$#}\"  ## 获得最后一个参数
     6    dmsg "last_arg=$last_arg"
     7    local tail=""
     8    case $last_arg in .[0-9]* )  ## 是否 ip?
     9       tail=$last_arg  ## 保存默认 host ip
    10       args_list="${*%${last_arg}}";;  ## 保存后移除
    11    esac
    12    
    13    local delim=","  ## 分隔符
    14    local args=""  ## 项中的单个参数
    15    local the_end=0  ## 末项处理标识
    16    while local seg="${args_list%%${delim}*}"  ## 2. 分割出每项
    18 local seg_b="" ## 用来传出的标准化项 19 [ "$the_end" -eq 0 ] ## 若末尾项还未处理(实现类似 do-while) 20 do 21 if [ -n "$seg" ]; then 22 dmsg "seg=$seg" 23 for args in $seg ## 3. 分割出每项中的参数,准备标准化 24 do 25 dmsg "args=$args" 26 case $args in 27 -* ) prev_opt="$args" 28 dmsg "prev_opt=$prev_opt" 29 ;; 30 * ) case $prev_opt in
    -*d ) ## 根据上次保存的选项做处理 31 if [ ! -d "$args" ]; then 32 args="${args%/*} ${args##*/}" ## 分离路径和文件名 33 fi
    ;;
    * ) if [ $(expr index "$args" "/") -gt 0 ];then ## 630需求
    [ ! -d "${args}" ] && args="${args%/*} ${args##*/}"
    args="-d ${args}"
    fi
    ;;
    34 esac 35 dmsg "seg_b=$seg_b" 36 prev_opt="" 37 ;; 38 esac 39 seg_b="$seg_b $args" ## 4. 生成标准化项 40 done 41 42 [ -n "$seg_b" ] && parse_opt $tail $seg_b ## 5. 最终处理(代入默认ip) 43 fi 44 if [ $(expr index "$args_list" "$delim") -eq 0 ]; then ## 已到最后一项 45 let the_end=the_end+1
    17 else
    args_list="${args_list#*${delim}}"  ## 需要处理的参数列表 46 fi 47 dmsg "the_end=$the_end" && echo 48 done ## 处理循环结束 49 }

    最后

    运行:tftp_multi_da "$@"

    收工:exit 0

    参考文献

    《Unix 编程艺术》

  • 相关阅读:
    快速幂模板
    部分有关素数的题
    POJ 3624 Charm Bracelet (01背包)
    51Nod 1085 背包问题 (01背包)
    POJ 1789 Truck History (Kruskal 最小生成树)
    HDU 1996 汉诺塔VI
    HDU 2511 汉诺塔X
    HDU 2175 汉诺塔IX (递推)
    HDU 2077 汉诺塔IV (递推)
    HDU 2064 汉诺塔III (递推)
  • 原文地址:https://www.cnblogs.com/lookbackinside/p/2570749.html
Copyright © 2011-2022 走看看