带有 for 循环和嵌套 if 语句的 Bash 函数给出了奇怪的结果

带有 for 循环和嵌套 if 语句的 Bash 函数给出了奇怪的结果

我有以下 bash 函数,它让我浪费了很多时间。如果我在 zenity 框中输入以下内容...

员工 ID = 2 类别 ID = 3

我得到以下信息:2 3

如果...

员工 ID = 类别 ID = 3

在第二个 zenity 窗口打开后,我输入 2 并得到以下信息:2 3

但是当我输入

员工 ID = 2 类别 ID =

没有打开其他 Zenity 窗口,我得到以下信息:2

我真正想要的是测试运行结束后的结果 2,3。

有人知道这里出了什么问题吗?

#!/bin/bash


num(){
        emp=$(echo "$1" | awk -F, -v  OFS=, '{print $1 "," $2}')

        IFS=, read -ra array1 <<<"$emp"
        
        p=$(for i in "${array1[@]}"
        
        do
                
                if [[ "${i}" =~ ^[0-9]+$ ]]; then

                        out="${i}"
                elif
                        [[ "${i}" = NULL ]]; then

                        out="${i}"


                else   local var2

                        until [[ ${var2} =~ ^[0-9]+$ ]] || [[ ${var2} = NULL ]]; do

                                var2="$(zenity --forms --title="table salaries_wages" --text "Add a number"  --separator="," \
                                --add-entry="WARNING! You either forgot to enter or didn't enter a number. Please enter a valid number: ")"

                         done

                                out="${var2}"

        fi

        echo "$out"
        
done)
      
        echo "$p"
}

input="$(zenity --forms --title="table salaries_wages" --text="Add a new salaries_wages entry" --separator="," \
        --add-entry="ENTER employeeid: " \
        --add-entry="ENTER categoryid: ")"

num "$input"

答案1

这里有什么问题?

你对工作原理的假设read与实际工作原理不同read。在 Bash 中运行此代码:

how_many () { IFS=, read -ra array1 <<<"$1"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"

您将得到2, 2, 1。最后一个数字很突出。这意味着尾随分隔符(,在本例中)read更像是一个终止符:它后面的空字段不会被读入数组,数组最终会少一个元素。如果您的代码中发生这种情况,那么for i in "${array1[@]}"将只对第一个字段运行循环。

解决办法可能是引入一个额外的 ,作为结尾的终止符。然后read永远不会读取第三个字段,但它始终会读取两个字段(即使第二个字段为空)。当我添加一个额外的时,请看区别,

how_many () { IFS=, read -ra array1 <<<"$1,"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"
how_many ","

输出是2每次。

要用这种方式修复代码,请使用<<<"$emp,"而不是<<<"$emp"


如果你修复了代码,那么当else块运行多次时(即当两个字段最初都是无效的),它就会出现错误;因为你重复使用var2变量再次。

我猜你以前local var2会避免这种情况,但local会将变量放在函数中,而不是放在else块中或循环的单次迭代中for。你var2在函数的同一个实例中重复使用num

你只调用了一次该函数。函数内部var2始终是相同的var2local只是使它与var2函数调用之外的任何变量不同。如果你var2在函数外部使用,函数中的变量将是不同的。如果你调用了num多次,每次调用都会使用自己不同的变量var2。这两种情况都不会发生。你只调用了一次该函数并重用了变量那里。当两个字段无效时,变量用于第一个字段,然后重用对于第二个字段。

但是如果你重建代码,那么一些函数(例如validate)就会在循环内被调用:

for i in "${array1[@]}"; do validate "$i"; …

local var2在函数中使用validate,那么var2在函数的每次调用中都会有所不同。这就是如何local提供帮助。在每个循环中validate都会重新调用,其局部变量将被重新初始化,而不会与具有相同名称的其他变量有任何联系。但是,您仍然可以重用局部变量里面以某种方式破坏某些东西的方式使用该函数(类似于您当前在函数var2中重用的方式num)。

在我写完上面的内容后,添加了一个例子到已经链接答案


注意p=$(stuff); echo "$p"几乎等同于echo "$(stuff)",这几乎总是应该的stuff。请阅读这个答案其中详细阐述var=$(stuff); echo "$var"

相关内容