
일부 데이터는 시간 간격에 걸쳐 분산되어 있으며 시간 간격 내에서 해당 데이터 중 일부를 가져오고 싶습니다. 예를 들어, 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
)하다분수 변수 값을 지원합니다. 그 중 하나에서 스크립트를 실행할 수 있다면 의 사용을 피할 수 있지만 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