
時間間隔にわたって分散しているデータがあり、その時間間隔内でそれらのデータの一部を取得したいと考えています。たとえば、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
。他の解決策もいくつか見つけましたが、非常に長くて不便なようですので、よりシンプルで読みやすい解決策を尋ねたいと思います。
コードが float で適切に動作することが重要です。私はこれに対する 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
関数は少なくとも 2 つの引数を取ります。1 つ目は your t_initial
、2 つ目は your ですt_final
。その後に、 your 、、、、の順に任意の数の引数をペアで取ることが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
小数は通常の 10 進数形式で指定できます。
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」が出力されるはずです。
あるいは、他のシェル(例えばzsh
)するは小数変数値をサポートします。スクリプトをこれらのいずれかで実行できる場合は、 の使用を避けることができますbc
が、比較は関数で行う方がまだ適切です。少なくとも のzsh
場合、それらは浮動小数点数なので、必ずしも正確ではありません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
。
もう 1 つの方法は、10 の累乗を掛けて数値を整数に正規化することです。この方法では、小数点以下の桁数が最大である必要があります。たとえば、小数点の右側の桁数が 3 桁を超えるデータ ポイントがない場合は、すべてに 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
小数点と 3 桁目以降のすべてを削除します。sed
コマンドは次s/\(.*\)\.\(...\).*/\1\2/
のような文字列を受け取ります。ABCデフ。ギクル
そして返品エイビーシーディーエフギなので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