いくつかのデータが時間間隔にわたって分散されており、時間間隔内でいくつかのデータを取得したいと思います。たとえば、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
関数には2つ以上のパラメータが必要です。最初のものはあなたのものであり、t_initial
2番目のものはあなたのものです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」を印刷する必要があります。
または他のシェル(例:zsh
)する10進変数の値をサポートします。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
3sed
番目の数字の後のすべての項目を削除します。この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