• 一站式搞定Bash脚本的参数处理问题


    以下是来自StackOverflow网站的答案中的代码,写的实在是太好了,引用在这里,以供查阅。


    第一段代码,这个例子展示了如何解析处理以空格分隔的参数(例如:–-option argument 这样的传参方式),不使用getopt和getopts函数来实现。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/bin/bash
    
    # Usage: /tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts
    
    POSITIONAL=()
    while [[ $# -gt 0 ]]
    do
    key="$1"
    
    case $key in
        -e|--extension)
        EXTENSION="$2"
        shift # past argument
        shift # past value
        ;;
        -s|--searchpath)
        SEARCHPATH="$2"
        shift # past argument
        shift # past value
        ;;
        -l|--lib)
        LIBPATH="$2"
        shift # past argument
        shift # past value
        ;;
        --default)
        DEFAULT=YES
        shift # past argument
        ;;
        *)    # unknown option
        POSITIONAL+=("$1") # save it in an array for later
        shift # past argument
        ;;
    esac
    done
    
    set -- "${POSITIONAL[@]}" # restore positional parameters
    
    echo "FILE EXTENSION  = ${EXTENSION}"
    echo "SEARCH PATH     = ${SEARCHPATH}"
    echo "LIBRARY PATH    = ${LIBPATH}"
    echo "DEFAULT         = ${DEFAULT}"
    echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
    if [[ -n $1 ]]; then
        echo "Last line of file specified as non-opt/last argument:"
        tail -1 "$1"
    fi
    

    一些说明:

    • POSITIONAL=()   ---  声明了一个空的数组。详见这里
    • $#  --- 这是一个表示参数个数的特殊变量。详见这里
    • $1, $2  --- 表示第一个参数,第二个参数的特殊变量。当然,$0表示第零个参数,一般保存的是脚本的名称。详见这里
    • “$1”  --- 被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同。被单引号用括住的内容,将被视为单一字串。在引号内的代表变量的$符号,没有作用,也就是说,它被视为一般符号处理,防止任何变量扩展。详见这里
    • shift --- Shift命令一次移动参数的个数由其所带的参数指定。如果不带参数,则每次运行shift, 销毁一个参数,后面的参数前移。详见这里
    • POSITIONAL+=("$1")  --- 向数组的最末的位置插入一个元素。详情见这里
    • ${POSITIONAL[@]}  --- 这个语法的意思是取回数组中的所有变量,${arr[@]}。详情见这里
    • tail –1 “$1”  --- tail 命令可用于查看文件的内容, tail –1 somefile.txt 会把somefile.txt的最后一行打印在屏幕上。
    • if [[ -n $1 ]];  ---  [[ 可以理解为更高级更现代的[, 即test命令。这里的语法对于用惯了高级语言的程序员会看起来很不习惯,感觉像if的条件里的比较少了一个操作数一样。把双方括号换成test命令就好理解了, 大概是这个样子:if (test –x option) then …
      • test 命令有很多参数.
        • -n 的意思是 “the length of STRING is nonzero”.
        • -f 的意思是 “FILE exists and is a regular file”.


    第二段代码,这个例子展示了如何门解析处理以等号分隔的参数(例如:–-option=argument 这样的传参方式),不使用getopt和getopts函数实现。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #!/bin/bash
    
    # Usage: /tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
    
    for i in "$@"
    do
    case $i in
        -e=*|--extension=*)
        EXTENSION="${i#*=}"
        shift # past argument=value
        ;;
        -s=*|--searchpath=*)
        SEARCHPATH="${i#*=}"
        shift # past argument=value
        ;;
        -l=*|--lib=*)
        LIBPATH="${i#*=}"
        shift # past argument=value
        ;;
        --default)
        DEFAULT=YES
        shift # past argument with no value
        ;;
        *)
              # unknown option
        ;;
    esac
    done
    echo "FILE EXTENSION  = ${EXTENSION}"
    echo "SEARCH PATH     = ${SEARCHPATH}"
    echo "LIBRARY PATH    = ${LIBPATH}"
    echo "DEFAULT         = ${DEFAULT}"
    echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
    if [[ -n $1 ]]; then
        echo "Last line of file specified as non-opt/last argument:"
        tail -1 $1
    fi
    

    一些说明:

    • ${i#*=}  --- 这里的语义通过上下文能看出来是移除了“-e=conf”这样一个字符串里的值为“-e=”的子字符串。简单的语法是${string#substring},即从string的前头开始,删除最短的substring的一个匹配,详情看这里的“Substring Removal”的部分。这个语句中的*是一个通配符(wildcard),*=的意思是任意字符直到遇到等号的子字符串。关于删除子字符串,这里给出了一个删除前缀和后缀的例子。


    第三段代码,使用getopts函数实现。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #!/bin/sh
    
    # Usage: /tmp/demo-getopts.sh -vf /etc/hosts foo bar
    
    # A POSIX variable
    # Reset in case getopts has been used previously in the shell.
    OPTIND=1         
    
    # Initialize our own variables:
    output_file=""
    verbose=0
    
    while getopts "h?vf:" opt; do
        case "$opt" in
        h|?)
            show_help
            exit 0
            ;;
        v)  verbose=1
            ;;
        f)  output_file=$OPTARG
            ;;
        esac
    done
    
    shift $((OPTIND-1))
    
    echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
    

    一些说明:

    • $((OPTIND-1)) --- 这条语句中有两个知识点:
      • $(( )) 的功能是进行算术运算,括号中的内容为数学表达式,使用 $(( )) 可以求数学表达式的值。这里的意思就是说会对OPTIND-1这个表达式进行算术运算,用以求值。详情请看这里。
      • OPTIND 函数getopts配合case来进行操作时有两个隐含变量:一个是OPTARG,用来取当前选项的值,另外一个是OPTIND,代表下一个要处理的元素位置。OPTIND是一个特殊的变量,它的初始值是1,每次getopts处理完一个命令参数后就递增它,得到getopts要处理的下一个参数。详情见这里这里
    • getopts "h?vf:"   ---  这里的意思是getops 接受-h –v –f 为合规选项,?问号的意思就是不合规的选项进来的时候,就会执行给问好设定好的代码,程序员可以利用这个机制给用户提供恰当的信息指导。冒号的意思是关掉系统默认的处理不合规的选项的方式(disable the default error handling of invalid options),建议关掉系统默认的处理不合规选项。

    对于这个使用getopts的实现方法:

    • 优势:
      • 可移植性更好,其他的类似的shell比如dash,可以兼容。
      • 能自动地处理多重单字母选项,比如-vf filename, 这也是Unix的典型的处理方法。
    • 劣势:
      • 如果不额外添加代码的话,只能处理短选项,比如-h可以,但--help 就不行。


    参考资料

    ==============

    https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash

    http://mywiki.wooledge.org/BashFAQ/035#getopts

  • 相关阅读:
    数独高阶技巧入门之六——ALS
    数独高阶技巧入门之七——AIC & Nice Loop
    数独-链的理解顺序
    数独高阶技巧入门之三——Fish
    数独·唯一性技巧(Uniqueness)-2
    游戏剧本从入门到放弃
    Electron和NW.js入门笔记
    Spring boot Access-Control-Allow-Origin 问题解决
    Materialize -- 基于Material Design的主流前端响应式框架
    Ubuntu 安装 nvm
  • 原文地址:https://www.cnblogs.com/awpatp/p/14136500.html
Copyright © 2020-2023  润新知