일부 데이터가 시간 간격에 걸쳐 분산되어 있고 시간 간격 내에서 일부 데이터를 가져오고 싶습니다. 예를 들어, 가끔 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"가 인쇄되어야 합니다.
또는 다른 쉘(예: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
.
또 다른 방법은 숫자에 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
과 세 번째 숫자 뒤의 모든 항목을 제거합니다. 이 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