【问题标题】:bash while loops not structured correctlybash while 循环结构不正确
【发布时间】:2014-11-02 03:44:58
【问题描述】:

我需要确认三个不同的用户输入,我正在通过“这是正确的:是或否”while 循环来确认。显然我的代码是错误的,因为虽然第一个循环正确执行,但我只测试这些循环的脚本在执行其他两个循环之前完成。请参阅下面的代码。任何见解将不胜感激。

#loop 1
while [ "$answer" != "Yes" ]
do
stuff
read answer
done

#loop 2
while [ "$answer" != "Yes" ]
do
stuff
read answer
done

#loop 3
while [ "$answer" != "Yes" ]
do
stuff
read answer
done

【问题讨论】:

    标签: bash loops while-loop


    【解决方案1】:

    $answer 包含Yes 时,第一个循环结束。然后第二个循环立即执行相同的测试,测试失败,所以循环立即结束。第三个循环也会发生同样的事情。

    你应该清除循环之间的$answer

    #loop 1
    while [ "$answer" != "Yes" ]
    do
    stuff
    read answer
    done
    
    answer=
    
    #loop 2
    while [ "$answer" != "Yes" ]
    do
    stuff
    read answer
    done
    
    answer=
    
    #loop 3
    while [ "$answer" != "Yes" ]
    do
    stuff
    read answer
    done
    

    【讨论】:

    • 您可以通过两种方式明确回答,answer= 如上所述,或者unset answer。两者都做同样的事情,这只是一个偏好问题。
    • @David C. Rankin - 使用 unset 关键字似乎是更好的工程。
    • 任何一种方式都可以。 1/2 的手册用 var= 解释,另外 1/2 使用 unset var。避免answer=yes 问题的一种方法是编写一个执行测试的testyn() 函数。这样,所有变量都是测试函数的本地变量,不会影响脚本的其余部分。
    • @Barmar,David C. Rankin - 嗯......不,在我的循环之间添加未设置的答案在我的脚本中不起作用,它只是测试这些循环。也许我应该澄清一下:我希望循环 1 一直运行到是。循环 2 运行直到是。循环 3 运行直到是。就目前而言,一旦 Loop 1 收到 Yes,运行时结束。
    • 请提供您的测试用例的工作版本。我也写一篇。正在发生的其他事情未在您的原始问题中显示。
    【解决方案2】:

    您可能希望对函数使用更结构化的方法(更 DRY)

    until_yes () {
        local stuff=$1
        local answer
        until [[ ${answer,,} == yes ]]; do
            $stuff
            read -p "Is this correct? (yes/no) " answer
        done
    }
    
    stuff1 () { ... }
    stuff2 () { ... }
    stuff3 () { ... }
    
    until_yes stuff1
    until_yes stuff2
    until_yes stuff3
    

    【讨论】:

    • 虽然上面和下面的答案最初允许我破解我正在编写的脚本,但您的代码显然是最简洁和结构化的。但是,您没有回答我的问题,这就是为什么我发布的代码部分不起作用,而不是如何让它起作用。
    【解决方案3】:

    鉴于我们在 cmets 中的讨论,这里有一个 unsetvar= 的快速示例以及一个 function tesyn,它显示了响应用户输入时测试的不同用途。该函数在yes 上返回0 (bash true),在no 上返回1 (bash false),允许在提问后进行简单的测试构造。

    #!/bin/bash
    
    function testyn {
        local answer
        read answer
        while : ; do
            answer=$(tr 'A-Z' 'a-z' <<<$answer)     # translate answer to lower-case
            case "$answer" in
    
                yes ) 
                    return 0
                    ;;
                no  ) 
                    return 1
                    ;;
                *   )
                    printf "    Invalid Input, Enter (yes/no): "
                    read answer
                    ;;
            esac
        done
    }
    
    printf "Continue? (yes/no): " 
    while read answer && [ "$answer" != yes ]; do
        printf "  Invalid input, (yes/no): "
    done
    
    unset answer
    
    printf "Continue? (yes/no): " 
    while read answer && [ "$answer" != yes ]; do
        printf "  Invalid input, (yes/no): "
    done
    
    answer=
    
    printf "Continue? (yes/no): " 
    while read answer && [ "$answer" != yes ]; do
        printf "  Invalid input, (yes/no): "
    done
    
    ## call function tesyn after a question, then
    #  use any test that tests for 1 or 0 to
    #  respond to the answer
    printf "\n  Would you like to continue? (y/n): "
    testyn || {
        printf "\nanswer was no, exiting...\n\n"
        exit 1
    }
    
    printf "\nAnswer must have been 'yes'. All Done!\n\n"
    
    exit 0
    

    使用/输出:

    $ bash testyn.sh
    Continue? (yes/no): maybe
      Invalid input, (yes/no): yes
    Continue? (yes/no): really
      Invalid input, (yes/no): yes
    Continue? (yes/no): yes
    
    Would you like to continue? (y/n): of_course
      Invalid Input, Enter (yes/no): ok
      Invalid Input, Enter (yes/no): yes
    
    Answer must have been 'yes'. All Done!
    

    【讨论】:

    • 如果 read 本身失败,您的测试全部失败:尝试在提示是/否时输入 EOF (Ctrl-D) 并享受您的美丽错误。
    • 目的不是为了捕获 EOF。你可以整天推ctrl+d,它对测试所做的就是重新打印提示行。输入(是/否)它会正常工作,输入其他任何内容,然后再次提示您。我敢肯定,你的讽刺与你的年龄和成熟度成正比。它不需要或不受欢迎。让我们与您的答案进行比较。
    • 目的是确保用户真的想要继续。如你真的要删除文件吗?现在你自己试试吧:printf "Continue? (yes/no): "; while read answer &amp;&amp; [ "$answer" != yes ]; do printf " Invalid input, (yes/no): "; done; echo "You typed yes"。提示时输入EOF,你会看到You typed yes会被回显,程序会愉快地继续。好吧,作为一个用户,我并不期待这个程序。现在,如果这是您想要的行为,您应该清楚地提及它,因为在我的书中这是一种危险的行为。
    猜你喜欢
    • 2014-10-31
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    • 1970-01-01
    • 2020-08-19
    • 2012-02-14
    相关资源
    最近更新 更多