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

相關內容