Bash if 区间内变量的条件

Bash if 区间内变量的条件

我有一些分布在时间间隔内的数据,并且我想在时间间隔内获取其中的一些数据。例如,我有时有 1..9、11..19 等范围内的数据,我想获取 1-2、然后 11-12 等范围内的数据。

这将是一个更复杂的脚本的一部分bash,我想在其中包含这个条件和一个if循环,以隔离可以捕获数据的时间。

我在想这样的事情:

if (( $t_initial & $t_final )) in (begin1, fin1) or in (begin2, fin2) ...

其中t_initialt_final是由脚本本身单独计算的。

我无法用bash语法写出这个条件。我找到了一些其他解决方案,但它们看起来非常长且不方便,所以我在这里询问更简单且更具可读性的解决方案。


代码与浮点数一起正常工作非常重要。我正在尝试修复OP解决方案,但仍然找不到方法。

答案1

不确定您是否喜欢这个解决方案。它有2个特点:

  • 不需要外部程序
  • 它使用一个函数,因此至少隐藏了比较的复杂性

就是这个:

#!/bin/bash

# The comparing function
function compareInterval {
 t1=$1
 t2=$2

 shift 2

 while (( "$2" )); do
   if ((  $t1 >= $1  &&  $t2 <= $2 )); then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2
t_final=4

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5; then
 echo Got match
fi

答案2

bash本身不支持范围比较,也不支持浮点数,因此我们必须自己做一些事情。我还将定义一个函数,并用于bc浮点计算。这是最终结果和测试套件:

# Call as `compareRanges start end b1 f1 b2 f2 b3 f3...`
compareRanges() {
    local t_initial=$1
    local t_final=$2
    shift 2
    while [ ${#@} -gt 1 ]
    do
        local in_range=$(bc <<<"$t_initial >= $1 && $t_final <= $2")
        if [ $in_range = 1 ]
        then
            # Debugging output to stderr - can be removed:
            echo "[$t_initial,$t_final] is within [$1,$2]" >&2
            return 0
        fi
        shift 2
    done
    # Debugging output to stderr - can be removed:
    echo "[$t_initial,$t_final] is not within any ranges." >&2
    return 1
}
# Basic integers from the example
compareRanges 1 3 2 4 && echo BAD || echo OK
compareRanges 1 3 1 3 && echo OK || echo BAD
compareRanges 1 3 0 4 && echo OK || echo BAD
# Fractional numbers
compareRanges 1.5 2.5 1.1 2.2 && echo BAD || echo OK
compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD
# Multiple ranges
compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD
compareRanges 5 7 1 2 3 4 5 6 7 8 && echo BAD || echo OK

compareRanges函数至少需要两个参数。第一个是你的t_initial,第二个也是你的t_final。之后它可以成对地采用任意多个其他参数,依次为begin1, fin1, begin2, fin2, 。

第一个测试用例比较问题注释中的范围:1-3 和 2-4。

compareRanges 1 3 2 4 && echo BAD || echo OK

如此,1如此,如此,如此。​t_initial3t_final2begin14fin1

当您想使用多个范围时,可以随后将它们成对列出:

compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD

这里我们针对 1-4、2-6 和 3-9 进行测试。在while循环中,我们依次查看每一对并将其与t_initial和进行比较t_final

因为bash不支持我们使用的小数bc,任意精度计算器。它的输入由以下部分给出<<<"$t_initial >= $1" ...:将字符串送入标准输入。$1是我们当前在循环迭代中查看的范围的开始,$2是结束;我们同时将下限和上限与 进行比较&&。当比较为真时将输出,当比较为假时bc将输出。我们将结果保存在 中,当我们的两个测试都为真时,该函数就会成功 ( )。10in_rangereturn 0

小数可以用普通的十进制形式指定:

compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD

bc将处理具有任意数量的小数位数和所需大小的数字。

最后,如果没有边界对匹配,我们就会失败(return 1)。您可以将该函数用作:

if compareRanges $t_initial $t_final 2 4 11 19
then
    ...
fi

当您运行测试套件时,它应该打印所有“OK”。


或者,其他 shell(例如zsh支持小数变量值。如果您可以在其中之一中运行脚本,则可以避免使用bc,尽管在函数中进行比较仍然更好。至少在zsh's 的情况下,它们是浮点数,因此它们不一定准确;bc永远是正确的。

答案3

以下是一些对 LatinSuD 处理浮点的答案的无耻抄袭。您会注意到他的回答夸口说“不需要外部程序”。这个程序使用计算器程序,bc正如他所建议的:

#!/bin/bash

# The comparing function
function compareInterval {
 t1=$1
 t2=$2

 shift 2

 while (( "$2" ))
 do
   # if ((  $t1 >= $1  &&  $t2 <= $2 ))
   bc_result=$(echo "print $t1 >= $1  &&  $t2 <= $2" | bc)
   if [  "$bc_result" = 1 ]
   then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2.3
t_final=4.2

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5
then
 echo Got match
fi

这只是进行if (( $t1 >= $1 && $t2 <= $2 ))测试并将其发送到bc,然后捕获 的输出bc


另一种方法是通过乘以 10 的幂将数字标准化为整数。这要求您具有最大小数位数。例如,如果没有数据点的小数点右侧超过三位,我们可以将所有数据乘以 1000。

#!/bin/bash

# Normalize function: it multiplies a floating point number by 1000
# without using floating point arithmetic.
normalize()
{
  case "$1" in
    *.*)
        result=$(echo "$1"000 | sed 's/\(.*\)\.\(...\).*/\1\2/')
        ;;
    *)
        result="$1"000
  esac
  echo "$result"
}

# The comparing function
function compareInterval {
 t1=$(normalize $1)
 t2=$(normalize $2)

 shift 2

 while (( "$2" ))
 do
   a1=$(normalize $1)
   a2=$(normalize $2)
   if ((  $t1 >= $a1  &&  $t2 <= $a2 ))
   then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2.3
t_final=4.2

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5
then
 echo Got match
fi

如果函数的参数normalize是一个简单的整数(即没有小数点的数字,例如17),我们可以简单地通过附加 来乘以 1000 000,所以1717000。如果 的参数normalize是浮点数(即包含小数点,例如42.5),我们仍然附加000, 然后使用sed删除小数点以及第三位之后的所有内容。该sed命令s/\(.*\)\.\(...\).*/\1\2/采用类似的字符串abcdef吉克尔 并返回abcdefghi,所以42.542.500042500(即 42.5 × 1000)。

可以完全在 中执行此字符串操作bash,而不使用sed.

答案4

您所询问的“11..19”称为大括号扩展。

你可以使用eval {$t_initial..$t_final}

...或者

if `seq $t_initial..$t_final`==$somevalue

相关内容