• shell编程流程控制语句case和select


    case 和 select 结构并不属于循环结构,因为它们并没有反复执行代码块。但是和循环结构相似的是,它们会根据代码块顶部或尾部的条件控制程序流。

    1、case (in) / esac

    在 shell 脚本中,case 模拟了 C/C++ 语言中的 switch,可以根据条件跳转到其中一个分支。其相当于简写版的 if/then/else 语句。很适合用来创建菜单选项哟!

    case "$variable" in
      "$condition1" )
        command...
      ;;
      "$condition2" )
        command...
      ;;
    esac
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对变量进行引用不是必须的,因为在这里不会进行字符分割。
    条件测试语句必须以右括号 ) 结束。[^1]
    每一段代码块都必须以双分号 ;; 结束。
    如果测试条件为真,其对应的代码块将被执行,而后整个 case 代码段结束执行。
    case 代码段必须以 esac 结束(倒着拼写case)。

    样例 11-25. 如何使用 case

    #!/bin/bash
    # 测试字符的种类。
    echo; echo "Hit a key, then hit return."
    read Keypress
    case "$Keypress" in
      [[:lower:]]   ) echo "Lowercase letter";;
      [[:upper:]]   ) echo "Uppercase letter";;
      [0-9]         ) echo "Digit";;
      *             ) echo "Punctuation, whitespace, or other";;
    esac      #  字符范围可以用[方括号]表示,也可以用 POSIX 形式的[[双方括号]]表示。
    # 在这个例子的第一个版本中,用来测试是小写还是大写字符使用的是 [a-z] 和 [A-Z]。
    # 这在一些特定的语言环境和 Linux 发行版中不起效。
    # POSIX 形式具有更好的兼容性。
    # 感谢 Frank Wang 指出这一点。
    # 练习:
    # -----
    # 这个脚本接受一个单字符然后结束。
    # 修改脚本,使得其可以循环接受输入,并且检测键入的每一个字符,直到键入 "X" 为止。
    # 提示:将所有东西包在 "while" 中。
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    样例 11-26. 使用 case 创建菜单

    #!/bin/bash
    # 简易的通讯录数据库
    clear # 清屏。
    echo "          Contact List"
    echo "          ------- ----"
    echo "Choose one of the following persons:" 
    echo
    echo "[E]vans, Roland"
    echo "[J]ones, Mildred"
    echo "[S]mith, Julie"
    echo "[Z]ane, Morris"
    echo
    read person
    case "$person" in
    # 注意变量是被引用的。
      "E" | "e" )
      # 同时接受大小写的输入。
      echo
      echo "Roland Evans"
      echo "4321 Flash Dr."
      echo "Hardscrabble, CO 80753"
      echo "(303) 734-9874"
      echo "(303) 734-9892 fax"
      echo "revans@zzy.net"
      echo "Business partner & old friend"
      ;;
      # 注意用双分号结束这一个选项。
      "J" | "j" )
      echo
      echo "Mildred Jones"
      echo "249 E. 7th St., Apt. 19"
      echo "New York, NY 10009"
      echo "(212) 533-2814"
      echo "(212) 533-9972 fax"
      echo "milliej@loisaida.com"
      echo "Ex-girlfriend"
      echo "Birthday: Feb. 11"
      ;;
      # Smith 和 Zane 的信息稍后添加。
      *         )
      # 缺省设置。
      # 空输入(直接键入回车)也是执行这一部分。
      echo
      echo "Not yet in database."
      ;;
    esac
    echo
    # 练习:
    # -----
    # 修改脚本,使得其可以循环接受多次输入而不是只显示一个地址后终止脚本。
    exit 0
    
    • 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
    • 48
    • 49
    • 50
    • 51

    你可以用 case 来检测命令行参数

    #!/bin/bash
    case "$1" in
      "") echo "Usage: ${0##*/} "; exit $E_PARAM;;
                          # 没有命令行参数,或者第一个参数为空。
                          # 注意 ${0##*/} 是参数替换 ${var##pattern} 的一种形式。
                          # 最后的结果是 $0.
      -*) FILENAME=./$1;; #  如果传入的参数以短横线开头,那么将其替换为 ./$1
                          #+ 以避免后续的命令将其解释为一个选项。
      * ) FILENAME=$1;;   # 否则赋值为 $1。
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    下面是一个更加直观的处理命令行参数的例子:

    #!/bin/bash
    while [ $# -gt 0 ]; do    # 遍历完所有参数
      case "$1" in
        -d|--debug)
                  # 检测是否是 "-d" 或者 "--debug"。
                  DEBUG=1
                  ;;
        -c|--conf)
                  CONFFILE="$2"
                  shift
                  if [ ! -f $CONFFILE ]; then
                    echo "Error: Supplied file doesn't exist!"
                    exit $E_CONFFILE     # 找不到文件。
                  fi
                  ;;
      esac
      shift       # 检测下一个参数
    done
    # 摘自 Stefano Falsetto 的 "Log2Rot" 脚本中 "rottlog" 包的一部分。
    # 已授权使用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    样例 11-27. 使用命令替换生成 case 变量

    #!/bin/bash
    # case-cmd.sh: 使用命令替换生成 "case" 变量。
    case $( arch ) in   # $( arch ) 返回设备架构。
                        # 等价于 'uname -m"。
      i386 ) echo "80386-based machine";;
      i486 ) echo "80486-based machine";;
      i586 ) echo "Pentium-based machine";;
      i686 ) echo "Pentium2+-based machine";;
      *    ) echo "Other type of machine";;
    esac
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    case 还可以用来做字符串模式匹配。
    样例 11-28. 简单的字符串匹配

    #!/bin/bash
    # match-string.sh: 使用 'case' 结构进行简单的字符串匹配。
    match_string ()
    { # 字符串精确匹配。
      MATCH=0
      E_NOMATCH=90
      PARAMS=2     # 需要2个参数。
      E_BAD_PARAMS=91
      [ $# -eq $PARAMS ] || return $E_BAD_PARAMS
      case "$1" in
        "$2") return $MATCH;;
        *   ) return $E_NOMATCH;;
      esac
    }
    a=one
    b=two
    c=three
    d=two
    match_string $a     # 参数个数不够
    echo $?             # 91
    match_string $a $b  # 匹配不到
    echo $?             # 90
    match_string $a $d  # 匹配成功
    echo $?             # 0
    exit 0
    
    • 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

    样例 11-29. 检查输入

    #!/bin/bash
    # isaplpha.sh: 使用 "case" 结构检查输入。
    SUCCESS=0
    FAILURE=1   #  以前是FAILURE=-1,
                #+ 但现在 Bash 不允许返回负值。
    isalpha ()  # 测试字符串的第一个字符是否是字母。
    {
    if [ -z "$1" ]                # 检测是否传入参数。
    then
      return $FAILURE
    fi
    case "$1" in
      [a-zA-Z]*) return $SUCCESS;;  # 是否以字母形式开始?
      *        ) return $FAILURE;;
    esac
    }             # 可以与 C 语言中的函数 "isalpha ()" 作比较。
    isalpha2 ()   # 测试整个字符串是否都是字母。
    {
      [ $# -eq 1 ] || return $FAILURE
      case $1 in
      *[!a-zA-Z]*|"") return $FAILURE;;
                   *) return $SUCCESS;;
      esac
    }
    isdigit ()    # 测试整个字符串是否都是数字。
    {             # 换句话说,也就是测试是否是一个整型变量。
      [ $# -eq 1 ] || return $FAILURE
      case $1 in
        *[!0-9]*|"") return $FAILURE;;
                  *) return $SUCCESS;;
      esac
    }
    check_var ()  # 包装后的 isalpha ()。
    {
    if isalpha "$@"
    then
      echo "\"$*\" begins with an alpha character."
      if isalpha2 "$@"
      then        # 其实没必要检查第一个字符是不是字母。
        echo "\"$*\" contains only alpha characters."
      else
        echo "\"$*\" contains at least one non-alpha character."
      fi
    else
      echo "\"$*\" begins with a non-alpha character."
                  # 如果没有传入参数同样同样返回“存在非字母”。
    fi
    echo
    }
    digit_check ()  # 包装后的 isdigit ()。
    {
    if isdigit "$@"
    then
      echo "\"$*\" contains only digits [0 - 9]."
    else
      echo "\"$*\" has at least one non-digit character."
    fi
    echo
    }
    a=23skidoo
    b=H3llo
    c=-What?
    d=What?
    e=$(echo $b)   # 命令替换。
    f=AbcDef
    g=27234
    h=27a34
    i=27.34
    check_var $a
    check_var $b
    check_var $c
    check_var $d
    check_var $e
    check_var $f
    check_var     # 如果不传入参数会发送什么?
    #
    digit_check $g
    digit_check $h
    digit_check $i
    exit 0        # S.C. 改进了本脚本。
    # 练习:
    # -----
    # 写一个函数 'isfloat ()' 来检测输入值是否是浮点数。
    # 提示:可以参考函数 'isdigit ()',在其中加入检测合法的小数点即可。
    
    • 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
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    2、select

    select 结构是学习自 Korn Shell。其同样可以用来构建菜单。

    select variable [in list]
    do
     command...
     break
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5

    而效果则是终端会提示用户输入列表中的一个选项。注意,select 默认使用提示字串3(Prompt String 3,$PS3, 即#?),但同样可以被修改。
    样例 11-30. 使用 select 创建菜单

    #!/bin/bash
    PS3='Choose your favorite vegetable: ' # 设置提示字串。
                                           # 否则默认为 #?。
    echo
    select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
    do
      echo
      echo "Your favorite veggie is $vegetable."
      echo "Yuck!"
      echo
      break  # 如果没有 'break' 会发生什么?
    done
    exit
    # 练习:
    # -----
    # 修改脚本,使得其可以接受其他输入而不是 "select" 语句中所指定的。
    # 例如,如果用户输入 "peas,",那么脚本会通知用户 "Sorry. That is not on the menu."
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如果 in list 被省略,那么 select 将会使用传入脚本的命令行参数($@)或者传入函数的参数作为 list。

    可以与 for variable [in list] 中 in list 被省略的情况做比较。

    样例 11-31. 在函数中使用 select 创建菜单

    #!/bin/bash
    PS3='Choose your favorite vegetable: '
    echo
    choice_of()
    {
    select vegetable
    # [in list] 被省略,因此 'select' 将会使用传入函数的参数作为 list。
    do
      echo
      echo "Your favorite veggie is $vegetable."
      echo "Yuck!"
      echo
      break
    done
    }
    choice_of beans rice carrorts radishes rutabaga spinach
    #         $1    $2   $3      $4       $5       $6
    #         传入了函数 choice_of()
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在写匹配行的时候,可以在左边加上左括号 (,使整个结构看起来更加优雅。

    case $( arch ) in   # $( arch ) 返回设备架构。
      ( i386 ) echo “80386-based machine”;;
    # ^      ^
      ( i486 ) echo “80486-based machine”;;
      ( i586 ) echo “Pentium-based machine”;;
      ( i686 ) echo “Pentium2+-based machine”;;
      (    * ) echo “Other type of machine”;;
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    28.云原生之服务网格ServiceMesh和istio
    简单几步让你的远程桌面更安全
    LVGL V8通过按键切换页面
    windows下cmd命令行模式中cd变换路径命令无效的解决办法
    CTF 全讲解:[SWPUCTF 2022 新生赛]webdog1__start
    机器学习 --- 决策树
    用于物体识别和跟踪的下游任务自监督学习-1-引言
    融云 Flutter SDK,跨平台开发的真香之选
    记一次 .NET 某外贸ERP 内存暴涨分析
    全日制和非全日制之争,看完六年前的这个文件心里就有数了
  • 原文地址:https://blog.csdn.net/javascript_good/article/details/132874464