Bash脚本、case语句和子菜单

3
我希望能运行一个脚本,其中包含一个case语句用于在多个选择列表(类似于子菜单)之间进行选择,并在各个脚本之间进行切换。
#!/bin/bash

echo "Your choice ?"
echo "1) Foo"
echo "2) Bar"
echo "3) Stuff"
read case;

case $case in

#and this is where I would like to allow 
#(here a simplified example I do not manage to code)

1) What script ?    # may I use another case statement ?
    a) Foo1;;
    b) Foo2;;
2) What script ?    # d°
    a) Bar1;;
    b) Bar2;;
    c) Bar3;;

esac

由于Foo1,Foo2,Bar1,Bar2和Bar3实际上是bash脚本,我想从脚本中调用sh Foo1等命令。

我该如何操作: 我可以在case语句中包含另一个case语句(如果选项很多,则比if语句更好)吗? 如何从一个脚本中调用另一个脚本?

感谢您的帮助。

ThG


要从脚本内部调用另一个脚本,只需像在命令行中一样调用该脚本的名称即可。 - chrisaycock
2个回答

5

是的,你可以嵌套case语句。你也应该考虑使用select语句代替所有这些echoread语句。

options=("Option 1" "Option 2" "Option3" "Quit")
optionsprompt='Please enter your choice: '

sub1=("Option 1 sub 1" "Option 1 sub 2")
sub1prompt='Please enter your choice: '

PS3=$optionsprompt
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "you chose choice 1"
            PS3=$sub1prompt
            select sub1opt in "${sub1[@]}"
            do
                case $sub1opt in
                    "Option 1 sub 1")
                        echo "you chose choice 2"
                        ;;
                    "Option 1 sub 2")
                        echo "you chose choice 2"
                        ;;
                 esac
            done
            ;;
        "Option 2")
            echo "you chose choice 2"
            ;;
        "Option 2")
            echo "you chose choice 3"
            ;;
        "Quit")
            break
            ;;
        *) echo invalid option;;
    esac
done

第一级菜单应该长这样:
1) Option 1
2) Option 2
3) Option3
4) Quit
Please enter your choice:

然而,这很快就会变得非常复杂。最好将其分解为函数,其中内部的 select/case 语句在单独的函数中或者被抽象化,以便您可以传递参数来控制它们。

要调用另一个脚本,您可能希望设置一个保存目录的变量。它看起来应该像这样:

basedir=/some/path

case ...
...
    $basedir/Foo    # call the script

非常感谢您的快速回复。我还没有完全“掌握”它,我需要仔细思考(作为一个学习者,我必须先做一些功课...);我会尽快回答,一旦我理解了(希望不要花太多时间)。 - ThG
@Rodrigue:经过认真思考,我有几次失误(例如,我忘记为我的脚本设置chmod X权限...),但是多亏了你,我们最终成功了。再次感谢。 - ThG

5

Dennis的回答应该能满足你的需求。为了让脚本更易读,下面是使用函数实现的示例:

function which_foo {
    local foo_options=("foo1" "foo2" "cancel")
    local PS3="Select foo: "

    select foo in "${foo_options[@]}"
    do
        case $REPLY in
            1) ./foo1.sh
               break ;;

            2) ./foo2.sh
               break ;;

            3) break ;;
        esac
    done
}

ACTIONS=("foo" "bar" "quit")
PS3="Select action: "

select action in "${ACTIONS[@]}"
do
    case $action in
         "foo") which_foo
                break ;;

         "bar") which_bar # Not present in this example
                break ;;

        "quit") break ;;
    esac
done

作为旁注,请注意:
  • which_foocase 使用 $REPLY,它会自动设置为用户选择的数字,而不是选项的文本。
  • 每个 case 选项中都使用了 break。如果没有它们,select 会使你进入一个循环,因此用户将被要求再次输入选择。
  • select 永远不会为您更改 PS3 的值,因此在菜单的不同级别之间切换时,您必须自己进行更改。

也感谢您(请参见我对Dennis的“答案”)...同样的回答。 - ThG
你可以在函数内部使用 local PS3,这样就不必保存和恢复“全局”的值。你还可以将 FOO_OPTIONS 放在函数内,并将其设为 local。虽然使用 $REPLY 而不是 select 变量(如你的示例中的 $action)是可行的,但代码的可读性会降低。如果使用 break,则某些情况下可能仍需要在外面放置一个 while。+1:对于给出变化并提供更多细节 - Dennis Williamson
我不知道local PS3。我已经相应地更新了我的示例。谢谢你的提示,Dennis。 - Rodrigue
@Dennis 和 @Rodrigue:我仔细思考了一下,有几次犯了错误(例如,我忘记为我的脚本设置 chmod X 权限...),但是多亏了你们,我最终成功了。再次感谢! - ThG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接