
我有一些分佈在時間間隔內的數據,我想在時間間隔內獲取其中的一些數據。例如,我有時有 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