Bash case语句重启无效输入

时间:2015-03-16 07:07:07

标签: bash case-statement

试图找出一种在case语句中返回的方法。例如,如果你运行....

while :
do 
    clear
    cat<<-EOF
    ======================
    Foo Bar Doo Dah Setup
    ======================

    (1) Foo
    (2) Bar
    (q) Quit

    ----------------------
    EOF
    read
    case $REPLY in
    "1")  foo="foo" ;;
    "2")  bar="bar" ;;
    "q")  break     ;;
     * )  echo "Invalid Option"
    esac
    sleep .5

    clear
    cat<<-EOF
    ======================
    Foo Bar Doo Dah Setup
    ======================

    (1) Doo
    (2) Dah
    (q) Quit

    ----------------------
    EOF
    read
    case $REPLY in
    "1")  doo="doo" ;;
    "2")  dah="dah" ;;
    "q")  break     ;;
     * )  echo "Invalid Option"
    esac
    sleep .5

done

...并输入&#34;无效选项&#34;那么你会发现它会转移到下一个案例,而不是重新评估案例。

解决方法并不是太糟糕,只需要将case语句嵌套到while循环中的if语句中......

while read; do
    if [ $REPLY -ge 1 -a $REPLY -le 2 ]; then
        case $REPLY in
        "1")  foo="foo" ;;
        "2")  bar="bar" ;;
        esac
        break
    elif [ $REPLY == q ]; then 
        break
    else
        echo "Invalid Option"
    fi
done

所说的似乎有点多,有人知道某些形式的循环控件从案例选择中重新运行案例陈述吗?

1 个答案:

答案 0 :(得分:3)

如果在继续下一个菜单之前需要菜单中的有效选项,那么您需要围绕选择过程进行循环。您可能还应计算失败并在连续几次失败后终止,例如10。

shell循环结构支持breakcontinue,并且可以选择后跟一个数字,表示应该断开多少循环级别(默认级别数为1)。

代码还应注意read检测到的EOF并终止循环。这是通过以下代码中的answer变量实现的。

这导致代码如下:

retries=0
max_retries=10
while [ $retries -lt $max_retries ]
do 
    clear
    cat <<-'EOF'
    ======================
    Foo Bar Doo Dah Setup
    ======================

    (1) Foo
    (2) Bar
    (q) Quit

    ----------------------
    EOF
    retries=0
    answer=no
    while read
    do
        case "$REPLY" in
        "1")  foo="foo"; answer=yes; break;;
        "2")  bar="bar"; answer=yes; break;;
        "q")  break 2;;  # Or exit, or return if the code is in a function
         * )  echo "Invalid Option ('$REPLY' given)" >&2
              if [ $((++retries)) -ge $max_retries ]; then break 2; fi
              ;;
        esac
    done
    if [ "$answer" = "no" ]; then break; fi   # EOF in read loop

    sleep .5

    clear
    cat <<-'EOF'
    ======================
    Foo Bar Doo Dah Setup
    ======================

    (1) Doo
    (2) Dah
    (q) Quit

    ----------------------
    EOF
    retries=0
    answer=no
    while read
    do
        case $REPLY in
        "1")  doo="doo"; answer=yes;;
        "2")  dah="dah"; answer=yes;;
        "q")  break 2;;
         * )  echo "Invalid Option ('$REPLY' given)"" >&2
              if [ $((++retries)) -ge $max_retries ]; then break 2; fi
              ;;
        esac
    done
    if [ "$answer" = "no" ]; then break; fi   # EOF in read loop
    sleep .5
    echo "$foo$bar $doo$dah"   # Do something with the entered information
done

我并不完全热衷read之后没有名字,尤其是因为它是Bash扩展而不是标准shell功能(POSIX read实用程序不提供默认值变量名称),省略名称会不必要地限制脚本的可移植性。

另请注意,here文档的开头标记用引号括起来,因此here文档的内容不受shell扩展的影响。 -表示从此文档和文档标记行的末尾删除了前导标签(但不是空格)。

第一个循环的问题中的代码也可以被修复&#39;使用continue而不是嵌套的while循环。但是,如果第二个循环只是重试第二个提示,继续外循环并跳过第一个菜单会很复杂。显示的解决方案是对称的,并且适当地隔离了两个提示中每个提示的输入。

我选择不在重试时重新回显菜单,但是也不难在这个代码上循环。使用:

是可行的
retries=0
answer=no
while   clear
        cat <<-'EOF'
        ======================
        Foo Bar Doo Dah Setup
        ======================

        (1) Foo
        (2) Bar
        (q) Quit

        ----------------------
        EOF
        read
do
    …processing $REPLY as before…
done

然而,这样做会导致许多人头疼,因为人们通常不会意识到在while语句之后你可以拥有一个命令列表,并且它只是控制循环是否继续的最后一个的退出状态是否是另一次迭代。

我个人避免使用shell脚本中的标签,因此我可能会创建变量来保存菜单:

menu1=$(cat <<-'EOF'
======================
Foo Bar Doo Dah Setup
======================

(1) Foo
(2) Bar
(q) Quit

----------------------
EOF
)

循环中的提示可能是:

while clear; echo "$menu1"; read

更容易理解(双引号至关重要)。如果&&命令运行良好并且退出成功,则可以使用;代替clear