我有一些分布在时间间隔内的数据,并且我想在时间间隔内获取其中的一些数据。例如,我有时有 1..9、11..19 等范围内的数据,我想获取 1-2、然后 11-12 等范围内的数据。
这将是一个更复杂的脚本的一部分bash
,我想在其中包含这个条件和一个if
循环,以隔离可以捕获数据的时间。
我在想这样的事情:
if (( $t_initial & $t_final )) in (begin1, fin1) or in (begin2, fin2) ...
其中t_initial
和t_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_initial
3
t_final
2
begin1
4
fin1
当您想使用多个范围时,可以随后将它们成对列出:
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
将输出。我们将结果保存在 中,当我们的两个测试都为真时,该函数就会成功 ( )。1
0
in_range
return 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
,所以17
→ 17000
。如果 的参数normalize
是浮点数(即包含小数点,例如42.5
),我们仍然附加000
, 然后使用sed
删除小数点以及第三位之后的所有内容。该sed
命令s/\(.*\)\.\(...\).*/\1\2/
采用类似的字符串abcdef。吉克尔
并返回abcdefghi,所以42.5
→ 42.5000
→ 42500
(即 42.5 × 1000)。
可以完全在 中执行此字符串操作bash
,而不使用sed
.
答案4
您所询问的“11..19”称为大括号扩展。
你可以使用eval {$t_initial..$t_final}
,
...或者
if `seq $t_initial..$t_final`==$somevalue