Bash 在有两个条件和一个for循环的while循环中挣扎

Bash 在有两个条件和一个for循环的while循环中挣扎,bash,shell,Bash,Shell,我正在尝试学习如何在bash中使用数组。我正在编写一个脚本,要求用户输入三个数字,并计算出最大的数字是多少。我希望脚本强制用户只输入数值。此外,我希望这三个数字应该是不同的。我使用数组来存储用户输入。这就是我到目前为止所做的: ## declare variables order=("first" "second" "third") answers=() RED='\033[0;31m' NC='\033[0m' # No Color numbersonly() { if [[

我正在尝试学习如何在bash中使用数组。我正在编写一个脚本,要求用户输入三个数字,并计算出最大的数字是多少。我希望脚本强制用户只输入数值。此外,我希望这三个数字应该是不同的。我使用数组来存储用户输入。这就是我到目前为止所做的:

## declare variables
order=("first" "second" "third")
answers=()
RED='\033[0;31m'
NC='\033[0m' # No Color

numbersonly() {
        if [[ ! $1 =~ ^[0-9]+$ ]]; then
        echo "${RED}$1 is not a valid number.${NC}"
        else
            answers+=("$input")
            break
        fi
}

inarray(){
    for e in ${answers[@]}; do
    if [[ $1 == $e ]]; then
    echo "${RED}Warning.${NC}"
    fi
    done
}

readnumber(){
for i in {1..3}; do
j=$(awk "BEGIN { print $i-1 }")
    while read -p "Enter the ${order[$j]} number: " input ; do      
    inarray $input
    numbersonly $input
    done
done
}

displayanswers(){
echo "Your numbers are: ${answers[@]}"
}

biggestnumber(){

if (( ${answers[0]} >= ${answers[1]} )); then
        biggest=${answers[0]}
        else
        biggest=${answers[1]}
        fi
if (( $biggest <= ${answers[2]} )); then
        biggest=${answers[2]}
        fi  
echo "The biggest number is: $biggest"
}

main(){
readnumber
displayanswers
biggestnumber
}

main

现在,我可以让脚本在用户输入之前输入的数字时显示警告,但是如果用户输入已经输入,我似乎找不到正确的语法来保持while循环。思考?

没有太多的研究,我从屏幕上跳下的想法就随之而来了。当心,我还没有真正测试过它。。。调试是留给学生的练习

建议使用更新的函数定义,因为您可以声明局部变量。定义不允许本地化变量

function inarray { local e; #don't muck up any variable e in caller ... } 提示:一旦所有输入出现,应停止输入循环。还可以添加:

if [ "" == "${input:-}" ]; then break; 请注意,如果变量值为空或包含许多shell元字符(如空格),则大量使用双引号可避免语法错误。我无法告诉您我通过在现有代码中添加引号修复了多少错误报告

[…]]表达式使用文件名测试,而不是正则表达式。最接近[[!$1=~^[0-9]+$];正在使用[[$1!=[0-9]*]&&[$1!=*[^0-9]*]]。
但我怀疑!expr>/dev/null$i:“[0-9][0-9]*$”;因为expr使用正则表达式,所以更符合您的需要。不要用[]s括起来。使用[0-9][0-9]*而不是[0-9]+as+使我在UNIX正则表达式的所有方言中都取得了不同程度的成功。

我找到了解决方法。我的问题有两个:1我没有意识到如果在while循环中嵌套了for循环,那么需要两个break语句来退出while循环;2在同一while循环中有两个函数,因此很难控制发生了什么。通过将inarray和numbersonly合并到一个新函数中,我解决了双重条件问题。新函数如下所示:

testing(){
for item in ${answers[*]}
do
    test "$item" == "$1" && { inlist="yes"; break; } || inlist="no"
done

if [[ $inlist == "yes" ]]; then
echo "${RED}$1 is already in list.${NC}"
else
        if [[ ! $1 =~ ^[0-9]+$ ]]; then
        echo "${RED}$1 is not a valid number.${NC}"
        else
            answers+=("$input")
            break
        fi
fi
}

检查值是否已经在数组中,更有效的方法是将值作为键进行索引。也就是说,values[$i]=1允许您将${values[$i]}作为O1操作进行检查,而无需迭代。不过,除此之外,这里的代码实在太多了,无法一眼看出您想要的流控制是什么。您能否构建一个尽可能短的代码来演示您正在询问的问题,并明确描述其当前行为和预期行为的差异?如果你只需要一个循环就可以重现你的问题,那么就不需要五个函数和一堆全局范围的赋值来把问题弄得一团糟。有一件事,当你在main中调用displayanswers时,你对displayanswers的调用会有一个额外的“h”。最大的号码应该在另一条线上,对吗?@Nic3500是的。我用@CharlesDuffy修改了它,你需要根据你所坚持的观点来简化代码。我也同意你需要考虑最小化你函数中的全局变量。排版和你的函数定义风格是老式的KornShell。关于函数声明和局部变量的不同样式,您所说的对KornShell是正确的,但对Bash不是正确的。在ksh中,根据函数的声明方式,可以从函数中获得不同的结果,而在bash中则不是这样。Bash支持向后兼容的typeset,但首选local或declare。哦,还有您的示例if语句缺少then。+量词是扩展正则表达式egrep、awk、Perl、Python、Ruby、Java、Bash等,而不是基本正则表达式vanilla grep和sed。这可能就是为什么你有时会有问题。grep和sed的现代版本有允许ERE的选项。KornShell对这两种语言都有不同的语法;for循环的主体已经是任意命令列表。没有必要定义nx,因为您可以简单地初始化n=1并在n是到上面的时候循环,如果感谢@cdarke,请进行编辑,但不幸的是,有时我需要编辑在旧shell上运行的老式脚本,所以我喜欢在任何地方都能工作的东西。这包括传统ksh到当前ksh。 for (( a = 0; a < ${#answer[*]}; ++a )); do
testing(){
for item in ${answers[*]}
do
    test "$item" == "$1" && { inlist="yes"; break; } || inlist="no"
done

if [[ $inlist == "yes" ]]; then
echo "${RED}$1 is already in list.${NC}"
else
        if [[ ! $1 =~ ^[0-9]+$ ]]; then
        echo "${RED}$1 is not a valid number.${NC}"
        else
            answers+=("$input")
            break
        fi
fi
}