Bash에서 도트로 구분된 버전 포맷의 두 문자열을 비교하는 방법은 무엇입니까?
문자열을 bash로 할 수 있는 를 들어 bash는 어떤 입니까?§:2.4.5 ★★★★★★★★★★★★★★★★★」2.8 ★★★★★★★★★★★★★★★★★」2.4.5.1
다음은 외부 유틸리티가 필요 없는 순수 Bash 버전입니다.
#!/bin/bash
vercomp () {
if [[ $1 == $2 ]]
then
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
# fill empty fields in ver1 with zeros
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
ver1[i]=0
done
for ((i=0; i<${#ver1[@]}; i++))
do
if [[ -z ${ver2[i]} ]]
then
# fill empty fields in ver2 with zeros
ver2[i]=0
fi
if ((10#${ver1[i]} > 10#${ver2[i]}))
then
return 1
fi
if ((10#${ver1[i]} < 10#${ver2[i]}))
then
return 2
fi
done
return 0
}
testvercomp () {
vercomp $1 $2
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
if [[ $op != $3 ]]
then
echo "FAIL: Expected '$3', Actual '$op', Arg1 '$1', Arg2 '$2'"
else
echo "Pass: '$1 $op $2'"
fi
}
# Run tests
# argument table format:
# testarg1 testarg2 expected_relationship
echo "The following tests should pass"
while read -r test
do
testvercomp $test
done << EOF
1 1 =
2.1 2.2 <
3.0.4.10 3.0.4.2 >
4.08 4.08.01 <
3.2.1.9.8144 3.2 >
3.2 3.2.1.9.8144 <
1.2 2.1 <
2.1 1.2 >
5.6.7 5.6.7 =
1.01.1 1.1.1 =
1.1.1 1.01.1 =
1 1.0 =
1.0 1 =
1.0.2.0 1.0.2 =
1..0 1.0 =
1.0 1..0 =
EOF
echo "The following test should fail (test the tester)"
testvercomp 1 1 '>'
테스트를 실행합니다.
$ . ./vercomp
The following tests should pass
Pass: '1 = 1'
Pass: '2.1 < 2.2'
Pass: '3.0.4.10 > 3.0.4.2'
Pass: '4.08 < 4.08.01'
Pass: '3.2.1.9.8144 > 3.2'
Pass: '3.2 < 3.2.1.9.8144'
Pass: '1.2 < 2.1'
Pass: '2.1 > 1.2'
Pass: '5.6.7 = 5.6.7'
Pass: '1.01.1 = 1.1.1'
Pass: '1.1.1 = 1.01.1'
Pass: '1 = 1.0'
Pass: '1.0 = 1'
Pass: '1.0.2.0 = 1.0.2'
Pass: '1..0 = 1.0'
Pass: '1.0 = 1..0'
The following test should fail (test the tester)
FAIL: Expected '>', Actual '=', Arg1 '1', Arg2 '1'
에서는 사용 안 함)이 coreutils-7(Ubuntu Karmic에서는 사용 안 함)이 있습니다.sort에는 '''가 합니다.-V"CHANGE: "CHANGE: "CHANGE: "CHANGE: " (CHANGE: ") " 。
verlte() {
[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
}
verlt() {
[ "$1" = "$2" ] && return 1 || verlte $1 $2
}
verlte 2.5.7 2.5.6 && echo "yes" || echo "no" # no
verlt 2.4.10 2.4.9 && echo "yes" || echo "no" # no
verlt 2.4.8 2.4.10 && echo "yes" || echo "no" # yes
verlte 2.5.6 2.5.6 && echo "yes" || echo "no" # yes
verlt 2.5.6 2.5.6 && echo "yes" || echo "no" # no
아마도 이것을 달성하기 위한 보편적으로 올바른 방법은 없을 것이다. Debian을 사용해 .dpkg --compare-versions <first> <relation> <second>.
printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
다음과 같은 기능이 있습니다.
2.4.5
2.4.5.1
2.8
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
다음과 같이 사용:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
(https://apple.stackexchange.com/a/123408/11374)에서 입수).
필드 수를 알고 있으면 -kn,n을 사용하여 매우 심플한 솔루션을 얻을 수 있습니다.
echo '2.4.5
2.8
2.4.5.1
2.10.2' | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g
2.4.5
2.4.5.1
2.8
2.10.2
이것은 버전 내 최대 4개의 필드에 대한 것입니다.
$ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
$ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello
hello
할 수 ..다음 알고리즘과 같이 비교합니다.버전이 같으면 10을 반환하고 버전1이 버전2보다 크면 11을 반환하고 그렇지 않으면 9를 반환합니다.
#!/bin/bash
do_version_check() {
[ "$1" == "$2" ] && return 10
ver1front=`echo $1 | cut -d "." -f -1`
ver1back=`echo $1 | cut -d "." -f 2-`
ver2front=`echo $2 | cut -d "." -f -1`
ver2back=`echo $2 | cut -d "." -f 2-`
if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then
[ "$ver1front" -gt "$ver2front" ] && return 11
[ "$ver1front" -lt "$ver2front" ] && return 9
[ "$ver1front" == "$1" ] || [ -z "$ver1back" ] && ver1back=0
[ "$ver2front" == "$2" ] || [ -z "$ver2back" ] && ver2back=0
do_version_check "$ver1back" "$ver2back"
return $?
else
[ "$1" -gt "$2" ] && return 11 || return 9
fi
}
do_version_check "$1" "$2"
- ★★
V솔루션, 없습니다 "bash", "bash", "bash" 입니다. - " " "
===!=<<=>★★★★★★★★★★★★★★★★★」>=(신호) - 레터 " " " " 。
1.5a < 1.5b - 비교: " " " " " " "
1.6 > 1.5b - 에서 오른쪽으로 읽습니다.
if V 1.5 '<' 1.6; then ....
<< 고객명 >>님
# Sample output
# Note: ++ (true) and __ (false) mean that V works correctly.
++ 3.6 '>' 3.5b
__ 2.5.7 '<=' 2.5.6
++ 2.4.10 '<' 2.5.9
__ 3.0002 '>' 3.0003.3
++ 4.0-RC2 '>' 4.0-RC1
<< 고객명 >>님
function V() # $1-a $2-op $3-$b
# Compare a and b as version strings. Rules:
# R1: a and b : dot-separated sequence of items. Items are numeric. The last item can optionally end with letters, i.e., 2.5 or 2.5a.
# R2: Zeros are automatically inserted to compare the same number of items, i.e., 1.0 < 1.0.1 means 1.0.0 < 1.0.1 => yes.
# R3: op can be '=' '==' '!=' '<' '<=' '>' '>=' (lexicographic).
# R4: Unrestricted number of digits of any item, i.e., 3.0003 > 3.0000004.
# R5: Unrestricted number of items.
{
local a=$1 op=$2 b=$3 al=${1##*.} bl=${3##*.}
while [[ $al =~ ^[[:digit:]] ]]; do al=${al:1}; done
while [[ $bl =~ ^[[:digit:]] ]]; do bl=${bl:1}; done
local ai=${a%$al} bi=${b%$bl}
local ap=${ai//[[:digit:]]} bp=${bi//[[:digit:]]}
ap=${ap//./.0} bp=${bp//./.0}
local w=1 fmt=$a.$b x IFS=.
for x in $fmt; do [ ${#x} -gt $w ] && w=${#x}; done
fmt=${*//[^.]}; fmt=${fmt//./%${w}s}
printf -v a $fmt $ai$bp; printf -v a "%s-%${w}s" $a $al
printf -v b $fmt $bi$ap; printf -v b "%s-%${w}s" $b $bl
case $op in
'<='|'>=' ) [ "$a" ${op:0:1} "$b" ] || [ "$a" = "$b" ] ;;
* ) [ "$a" $op "$b" ] ;;
esac
}
코드 설명
행 1: 로컬 변수 정의:
a,op,b및 즉 .6 "3.5a - "3.6" > "3.5a"입니다.al,bl(의 뒷면)a★★★★★★★★★★★★★★★★★」b「와 같이 아이템으로 됩니다.「 6 」 、 「 5a 。
줄 2, 3: 꼬리 항목에서 왼쪽 자릿수이므로 문자(있는 경우 " 및 "a")만 남습니다.
줄 4: 오른쪽 자르기 문자a ★★★★★★★★★★★★★★★★★」b 변수로 ai ★★★★★★★★★★★★★★★★★」bi "3.6과 "3.6'은 '3.5')입니다4.01-RC2 > 4.01-RC1 ai ai = " 4.01" al = "RC2" 4 bi = " 4.01" bl = "RC1"이다.
행 6: 로컬 변수 정의:
ap,bp없음 - 오른쪽 패딩 없음ai★★★★★★★★★★★★★★★★★」bi하고, 그는 요소의 . 의 요소 수와 같습니다.a★★★★★★★★★★★★★★★★★」b각각 다음과 같다.
줄 7: 각 점 뒤에 "0"을 추가하여 패딩 마스크를 만듭니다.
9행: 로컬 변수:
w폭 - ★★★★fmt문자열,할 - printf 형식 문자열, printf 형식 문자열x(temporary)- ★★★★★★★★★★★★★★★★
IFS=.는 변수 . . . 는 ' . . 서 、 수 、 수 、 bash bash bash bash bash bash bash bash bash bash bash 。
행 10: 계산w사전 비교를 위해 항목을 정렬하는 데 사용되는 최대 항목 너비.w=2로 하다
행 11: printf 얼라인먼트 포맷을 작성하려면 다음 각 문자를 바꿉니다.$a.$b%${w}s"3.6" > "3.5a" %2s%2s%2s%2s%2s%2s%2s%2s이다.
행 12: "printf -v a"는 변수 값을 설정합니다.a이은 '먹다'에 합니다.a=sprintf(...)츠미야으로 IFS=에 대한 과 같습니다.printf별별항항항나나나나
번째 첫과 함께printf이 of의 a이 붙어 있고, 「0」에는 충분한 의 항목이 되어 있습니다.bp 문자열이 "" " " " " " " " " " 가 .a의 '할 수 있다'와하게 비교할 수 .b.
「 」는, 「 」를 하고 있는 것에 해 주세요.bp안 돼 - 안 돼ap로로 합니다.aiap ★★★★★★★★★★★★★★★★★」bp 수 으로 른른른른 may may may may may may 、 may may may may may may may may 。a ★★★★★★★★★★★★★★★★★」b이가가같같같같
번째와 함께printf.al로로 합니다.a의미 있는 비교를 가능하게 하는 충분한 패딩이 있습니다., 이제a가 가능합니다.b.
회선 13: 회선 12와 동일하지만b.
라인 15: 비빌트인 간 비교 사례 분할)<= ★★★★★★★★★★★★★★★★★」>=및 오퍼레이터 및 빌트인 연산자세한 내용은 다음과 같습니다.
16행: 비교 연산자가<= 후 테스트합니다.a<b or a=b - 각 ->= a<b or a=b
라인 17: 삽입 비교 연산자에 대한 검정.
<< 고객명 >>님
# All tests
function P { printf "$@"; }
function EXPECT { printf "$@"; }
function CODE { awk $BASH_LINENO'==NR{print " "$2,$3,$4}' "$0"; }
P 'Note: ++ (true) and __ (false) mean that V works correctly.\n'
V 2.5 '!=' 2.5 && P + || P _; EXPECT _; CODE
V 2.5 '=' 2.5 && P + || P _; EXPECT +; CODE
V 2.5 '==' 2.5 && P + || P _; EXPECT +; CODE
V 2.5a '==' 2.5b && P + || P _; EXPECT _; CODE
V 2.5a '<' 2.5b && P + || P _; EXPECT +; CODE
V 2.5a '>' 2.5b && P + || P _; EXPECT _; CODE
V 2.5b '>' 2.5a && P + || P _; EXPECT +; CODE
V 2.5b '<' 2.5a && P + || P _; EXPECT _; CODE
V 3.5 '<' 3.5b && P + || P _; EXPECT +; CODE
V 3.5 '>' 3.5b && P + || P _; EXPECT _; CODE
V 3.5b '>' 3.5 && P + || P _; EXPECT +; CODE
V 3.5b '<' 3.5 && P + || P _; EXPECT _; CODE
V 3.6 '<' 3.5b && P + || P _; EXPECT _; CODE
V 3.6 '>' 3.5b && P + || P _; EXPECT +; CODE
V 3.5b '<' 3.6 && P + || P _; EXPECT +; CODE
V 3.5b '>' 3.6 && P + || P _; EXPECT _; CODE
V 2.5.7 '<=' 2.5.6 && P + || P _; EXPECT _; CODE
V 2.4.10 '<' 2.4.9 && P + || P _; EXPECT _; CODE
V 2.4.10 '<' 2.5.9 && P + || P _; EXPECT +; CODE
V 3.4.10 '<' 2.5.9 && P + || P _; EXPECT _; CODE
V 2.4.8 '>' 2.4.10 && P + || P _; EXPECT _; CODE
V 2.5.6 '<=' 2.5.6 && P + || P _; EXPECT +; CODE
V 2.5.6 '>=' 2.5.6 && P + || P _; EXPECT +; CODE
V 3.0 '<' 3.0.3 && P + || P _; EXPECT +; CODE
V 3.0002 '<' 3.0003.3 && P + || P _; EXPECT +; CODE
V 3.0002 '>' 3.0003.3 && P + || P _; EXPECT _; CODE
V 3.0003.3 '<' 3.0002 && P + || P _; EXPECT _; CODE
V 3.0003.3 '>' 3.0002 && P + || P _; EXPECT +; CODE
V 4.0-RC2 '>' 4.0-RC1 && P + || P _; EXPECT +; CODE
V 4.0-RC2 '<' 4.0-RC1 && P + || P _; EXPECT _; CODE
보다 더 알 수 것 , 는 그 이 다른 버전보다 낮은지 하러 왔다.sort --version-sort버전 문자열 순서를 변경합니다.
string="$1
$2"
[ "$string" == "$(sort --version-sort <<< "$string")" ]
$ for OVFTOOL_VERSION in "4.2.0" "4.2.1" "5.2.0" "3.2.0" "4.1.9" "4.0.1" "4.3.0" "4.5.0" "4.2.1" "30.1.0" "4" "5" "4.1" "4.3"
> do
> if [ $(echo "$OVFTOOL_VERSION 4.2.0" | tr " " "\n" | sort --version-sort | head -n 1) = 4.2.0 ]; then
> echo "$OVFTOOL_VERSION is >= 4.2.0";
> else
> echo "$OVFTOOL_VERSION is < 4.2.0";
> fi
> done
4.2.0 is >= 4.2.0
4.2.1 is >= 4.2.0
5.2.0 is >= 4.2.0
3.2.0 is < 4.2.0
4.1.9 is < 4.2.0
4.0.1 is < 4.2.0
4.3.0 is >= 4.2.0
4.5.0 is >= 4.2.0
4.2.1 is >= 4.2.0
30.1.0 is >= 4.2.0
4 is < 4.2.0
5 is >= 4.2.0
4.1 is < 4.2.0
4.3 is >= 4.2.0
Dennis Williamson과 같은 결과를 반환하지만 회선을 적게 사용하는 함수를 구현했습니다. 체크를 합니다.★★★★★★★★★★★★★★★★,1..0하지만 다른 모든 테스트는 다음 코드로 통과한다.
#!/bin/bash
version_compare() {
if [[ $1 =~ ^([0-9]+\.?)+$ && $2 =~ ^([0-9]+\.?)+$ ]]; then
local l=(${1//./ }) r=(${2//./ }) s=${#l[@]}; [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}
for i in $(seq 0 $((s - 1))); do
[[ ${l[$i]} -gt ${r[$i]} ]] && return 1
[[ ${l[$i]} -lt ${r[$i]} ]] && return 2
done
return 0
else
echo "Invalid version number given"
exit 1
fi
}
외부 명령어를 사용하지 않는 간단한 Bash 함수를 다음에 나타냅니다.최대 3개의 숫자 부분이 포함된 버전 문자열에도 사용할 수 있습니다. 3개 미만도 상관없습니다.더 많은 비용으로 쉽게 확장할 수 있습니다.=,<,<=,>,>= , , , , 입니다.!=★★★★★★★★★★★★★★★★★★.
#!/bin/bash
vercmp() {
version1=$1 version2=$2 condition=$3
IFS=. v1_array=($version1) v2_array=($version2)
v1=$((v1_array[0] * 100 + v1_array[1] * 10 + v1_array[2]))
v2=$((v2_array[0] * 100 + v2_array[1] * 10 + v2_array[2]))
diff=$((v2 - v1))
[[ $condition = '=' ]] && ((diff == 0)) && return 0
[[ $condition = '!=' ]] && ((diff != 0)) && return 0
[[ $condition = '<' ]] && ((diff > 0)) && return 0
[[ $condition = '<=' ]] && ((diff >= 0)) && return 0
[[ $condition = '>' ]] && ((diff < 0)) && return 0
[[ $condition = '>=' ]] && ((diff <= 0)) && return 0
return 1
}
테스트는 다음과 같습니다.
for tv1 in '*' 1.1.1 2.5.3 7.3.0 0.5.7 10.3.9 8.55.32 0.0.1; do
for tv2 in 3.1.1 1.5.3 4.3.0 0.0.7 0.3.9 11.55.32 10.0.0 '*'; do
for c in '=' '>' '<' '>=' '<=' '!='; do
vercmp "$tv1" "$tv2" "$c" && printf '%s\n' "$tv1 $c $tv2 is true" || printf '%s\n' "$tv1 $c $tv2 is false"
done
done
done
테스트 출력의 서브셋:
<snip>
* >= * is true
* <= * is true
* != * is true
1.1.1 = 3.1.1 is false
1.1.1 > 3.1.1 is false
1.1.1 < 3.1.1 is true
1.1.1 >= 3.1.1 is false
1.1.1 <= 3.1.1 is true
1.1.1 != 3.1.1 is true
1.1.1 = 1.5.3 is false
1.1.1 > 1.5.3 is false
1.1.1 < 1.5.3 is true
1.1.1 >= 1.5.3 is false
1.1.1 <= 1.5.3 is true
1.1.1 != 1.5.3 is true
1.1.1 = 4.3.0 is false
1.1.1 > 4.3.0 is false
<snip>
도 ★★★★★★★★★★★★★★★★★★.pure bashprintf는 bash입니다.
function ver()
# Description: use for comparisons of version strings.
# $1 : a version string of form 1.2.3.4
# use: (( $(ver 1.2.3.4) >= $(ver 1.2.3.3) )) && echo "yes" || echo "no"
{
printf "%02d%02d%02d%02d" ${1//./ }
}
이것은 Dennis Williamson이 게시한 답변에 기초한 리비전(1.0-r1 등)을 지원하는 순수 Bash 솔루션입니다.'-RC1'과 같은 것을 지원하도록 쉽게 수정하거나 정규 표현을 변경하여 보다 복잡한 문자열에서 버전을 추출할 수 있습니다.
실장의 상세한 것에 대하여는, 인코드의 코멘트를 참조해 주세요.또, 부속의 디버깅 코드를 유효하게 해 주세요.
#!/bin/bash
# Compare two version strings [$1: version string 1 (v1), $2: version string 2 (v2)]
# Return values:
# 0: v1 == v2
# 1: v1 > v2
# 2: v1 < v2
# Based on: https://stackoverflow.com/a/4025065 by Dennis Williamson
function compare_versions() {
# Trivial v1 == v2 test based on string comparison
[[ "$1" == "$2" ]] && return 0
# Local variables
local regex="^(.*)-r([0-9]*)$" va1=() vr1=0 va2=() vr2=0 len i IFS="."
# Split version strings into arrays, extract trailing revisions
if [[ "$1" =~ ${regex} ]]; then
va1=(${BASH_REMATCH[1]})
[[ -n "${BASH_REMATCH[2]}" ]] && vr1=${BASH_REMATCH[2]}
else
va1=($1)
fi
if [[ "$2" =~ ${regex} ]]; then
va2=(${BASH_REMATCH[1]})
[[ -n "${BASH_REMATCH[2]}" ]] && vr2=${BASH_REMATCH[2]}
else
va2=($2)
fi
# Bring va1 and va2 to same length by filling empty fields with zeros
(( ${#va1[@]} > ${#va2[@]} )) && len=${#va1[@]} || len=${#va2[@]}
for ((i=0; i < len; ++i)); do
[[ -z "${va1[i]}" ]] && va1[i]="0"
[[ -z "${va2[i]}" ]] && va2[i]="0"
done
# Append revisions, increment length
va1+=($vr1)
va2+=($vr2)
len=$((len+1))
# *** DEBUG ***
#echo "TEST: '${va1[@]} (?) ${va2[@]}'"
# Compare version elements, check if v1 > v2 or v1 < v2
for ((i=0; i < len; ++i)); do
if (( 10#${va1[i]} > 10#${va2[i]} )); then
return 1
elif (( 10#${va1[i]} < 10#${va2[i]} )); then
return 2
fi
done
# All elements are equal, thus v1 == v2
return 0
}
# ---------- everything below this line is just for testing ----------
# Test compare_versions [$1: version string 1, $2: version string 2, $3: expected result]
function test_compare_versions() {
local op
compare_versions "$1" "$2"
case $? in
0) op="==" ;;
1) op=">" ;;
2) op="<" ;;
esac
if [[ "$op" == "$3" ]]; then
echo -e "\e[1;32mPASS: '$1 $op $2'\e[0m"
else
echo -e "\e[1;31mFAIL: '$1 $3 $2' (result: '$1 $op $2')\e[0m"
fi
}
echo -e "\nThe following tests should pass:"
while read -r test; do
test_compare_versions $test
done << EOF
1 1 ==
2.1 2.2 <
3.0.4.10 3.0.4.2 >
4.08 4.08.01 <
3.2.1.9.8144 3.2 >
3.2 3.2.1.9.8144 <
1.2 2.1 <
2.1 1.2 >
5.6.7 5.6.7 ==
1.01.1 1.1.1 ==
1.1.1 1.01.1 ==
1 1.0 ==
1.0 1 ==
1.0.2.0 1.0.2 ==
1..0 1.0 ==
1.0 1..0 ==
1.0-r1 1.0-r3 <
1.0-r9 2.0 <
3.0-r15 3.0-r9 >
...-r1 ...-r2 <
2.0-r1 1.9.8.21-r2 >
1.0 3.8.9.32-r <
-r -r3 <
-r3 -r >
-r3 -r3 ==
-r -r ==
0.0-r2 0.0.0.0-r2 ==
1.0.0.0-r2 1.0-r2 ==
0.0.0.1-r7 -r9 >
0.0-r0 0 ==
1.002.0-r6 1.2.0-r7 <
001.001-r2 1.1-r2 ==
5.6.1-r0 5.6.1 ==
EOF
echo -e "\nThe following tests should fail:"
while read -r test; do
test_compare_versions $test
done << EOF
1 1 >
3.0.5-r5 3..5-r5 >
4.9.21-r3 4.8.22-r9 <
1.0-r 1.0-r1 ==
-r 1.0-r >
-r1 0.0-r1 <
-r2 0-r2 <
EOF
Busy Box에서 임베디드 Linux(Yocto)를 사용하고 있습니다.BusyBox에는 다음이 없습니다.-V옵션(단, BusyBox는 정규 표현을 수행할 수 있습니다).그래서 그 제약조건에 맞는 Bash 버전 비교가 필요했어요.
나는 다음과 같은 (Dennis Williamson의 답변과 유사)을 만들어 "자연 소트" 타입의 알고리즘을 사용하여 비교했다.숫자 부분과 숫자 이외의 부분으로 문자열을 분할하여 숫자 부분을 비교합니다(따라서).10 9ASC 로서 「」및 「」, 「asciii」를 사용합니다.II의 플레인 비교로서 수치 이외의 부품을 비교합니다.
ascii_frag() {
expr match "$1" "\([^[:digit:]]*\)"
}
ascii_remainder() {
expr match "$1" "[^[:digit:]]*\(.*\)"
}
numeric_frag() {
expr match "$1" "\([[:digit:]]*\)"
}
numeric_remainder() {
expr match "$1" "[[:digit:]]*\(.*\)"
}
vercomp_debug() {
OUT="$1"
#echo "${OUT}"
}
# return 1 for $1 > $2
# return 2 for $1 < $2
# return 0 for equal
vercomp() {
local WORK1="$1"
local WORK2="$2"
local NUM1="", NUM2="", ASCII1="", ASCII2=""
while true; do
vercomp_debug "ASCII compare"
ASCII1=`ascii_frag "${WORK1}"`
ASCII2=`ascii_frag "${WORK2}"`
WORK1=`ascii_remainder "${WORK1}"`
WORK2=`ascii_remainder "${WORK2}"`
vercomp_debug "\"${ASCII1}\" remainder \"${WORK1}\""
vercomp_debug "\"${ASCII2}\" remainder \"${WORK2}\""
if [ "${ASCII1}" \> "${ASCII2}" ]; then
vercomp_debug "ascii ${ASCII1} > ${ASCII2}"
return 1
elif [ "${ASCII1}" \< "${ASCII2}" ]; then
vercomp_debug "ascii ${ASCII1} < ${ASCII2}"
return 2
fi
vercomp_debug "--------"
vercomp_debug "Numeric compare"
NUM1=`numeric_frag "${WORK1}"`
NUM2=`numeric_frag "${WORK2}"`
WORK1=`numeric_remainder "${WORK1}"`
WORK2=`numeric_remainder "${WORK2}"`
vercomp_debug "\"${NUM1}\" remainder \"${WORK1}\""
vercomp_debug "\"${NUM2}\" remainder \"${WORK2}\""
if [ -z "${NUM1}" -a -z "${NUM2}" ]; then
vercomp_debug "blank 1 and blank 2 equal"
return 0
elif [ -z "${NUM1}" -a -n "${NUM2}" ]; then
vercomp_debug "blank 1 less than non-blank 2"
return 2
elif [ -n "${NUM1}" -a -z "${NUM2}" ]; then
vercomp_debug "non-blank 1 greater than blank 2"
return 1
fi
if [ "${NUM1}" -gt "${NUM2}" ]; then
vercomp_debug "num ${NUM1} > ${NUM2}"
return 1
elif [ "${NUM1}" -lt "${NUM2}" ]; then
vercomp_debug "num ${NUM1} < ${NUM2}"
return 2
fi
vercomp_debug "--------"
done
}
다음과 같은 복잡한 버전 번호를 비교할 수 있습니다.
1.2-r3vs 대1.2-r41.2rc3vs 대1.2r4
Dennis Williamson의 답변에서 일부 코너 케이스에 대해 동일한 결과를 반환하지 않습니다.특히:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
하지만 그건 코너 케이스고 결과는 여전히 합리적이라고 생각합니다.
다들 복잡한 해결책을 제시했잖아요여기 더 간단한 것이 있습니다.
function compare_versions {
local a=${1%%.*} b=${2%%.*}
[[ "10#${a:-0}" -gt "10#${b:-0}" ]] && return 1
[[ "10#${a:-0}" -lt "10#${b:-0}" ]] && return 2
a=${1:${#a} + 1} b=${2:${#b} + 1}
[[ -z $a && -z $b ]] || compare_versions "$a" "$b"
}
: 사법 usage :compare_versions <ver_a> <ver_b>
코드 '''」1이 두 번째 버전보다 입니다.2는 적고, 「」는 「」입니다.0하다
또한 비재귀 버전:
function compare_versions {
local a=$1 b=$2 x y
while [[ $a || $b ]]; do
x=${a%%.*} y=${b%%.*}
[[ "10#${x:-0}" -gt "10#${y:-0}" ]] && return 1
[[ "10#${x:-0}" -lt "10#${y:-0}" ]] && return 2
a=${a:${#x} + 1} b=${b:${#y} + 1}
done
return 0
}
다음은 상위 답변(Dennis)을 보다 간결하고 다른 반환값 체계를 사용하여 단일 비교로 쉽게 구현할 수 있도록 개선한 것입니다.또한 [0-9]가 아닌 첫 번째 문자 뒤의 모든 것을 사전 편찬적으로 비교하므로 1.0rc1 < 1.0rc2입니다.
# Compares two tuple-based, dot-delimited version numbers a and b (possibly
# with arbitrary string suffixes). Returns:
# 1 if a<b
# 2 if equal
# 3 if a>b
# Everything after the first character not in [0-9.] is compared
# lexicographically using ASCII ordering if the tuple-based versions are equal.
compare_versions() {
if [[ $1 == "$2" ]]; then
return 2
fi
local IFS=.
local i a=(${1%%[^0-9.]*}) b=(${2%%[^0-9.]*})
local arem=${1#${1%%[^0-9.]*}} brem=${2#${2%%[^0-9.]*}}
for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
return 1
elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
return 3
fi
done
if [ "$arem" '<' "$brem" ]; then
return 1
elif [ "$arem" '>' "$brem" ]; then
return 3
fi
return 2
}
@gammazero의 코멘트에 대처하기 위해 (내 생각에) 시멘틱버전화와 호환성이 있는 긴 버전은 다음과 같습니다.
# Compares two dot-delimited decimal-element version numbers a and b that may
# also have arbitrary string suffixes. Compatible with semantic versioning, but
# not as strict: comparisons of non-semver strings may have unexpected
# behavior.
#
# Returns:
# 1 if a<b
# 2 if equal
# 3 if a>b
compare_versions() {
local LC_ALL=C
# Optimization
if [[ $1 == "$2" ]]; then
return 2
fi
# Compare numeric release versions. Supports an arbitrary number of numeric
# elements (i.e., not just X.Y.Z) in which unspecified indices are regarded
# as 0.
local aver=${1%%[^0-9.]*} bver=${2%%[^0-9.]*}
local arem=${1#$aver} brem=${2#$bver}
local IFS=.
local i a=($aver) b=($bver)
for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
return 1
elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
return 3
fi
done
# Remove build metadata before remaining comparison
arem=${arem%%+*}
brem=${brem%%+*}
# Prelease (w/remainder) always older than release (no remainder)
if [ -n "$arem" -a -z "$brem" ]; then
return 1
elif [ -z "$arem" -a -n "$brem" ]; then
return 3
fi
# Otherwise, split by periods and compare individual elements either
# numerically or lexicographically
local a=(${arem#-}) b=(${brem#-})
for ((i=0; i<${#a[@]} && i<${#b[@]}; i++)); do
local anns=${a[i]#${a[i]%%[^0-9]*}} bnns=${b[i]#${b[i]%%[^0-9]*}}
if [ -z "$anns$bnns" ]; then
# Both numeric
if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
return 1
elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
return 3
fi
elif [ -z "$anns" ]; then
# Numeric comes before non-numeric
return 1
elif [ -z "$bnns" ]; then
# Numeric comes before non-numeric
return 3
else
# Compare lexicographically
if [[ ${a[i]} < ${b[i]} ]]; then
return 1
elif [[ ${a[i]} > ${b[i]} ]]; then
return 3
fi
fi
done
# Fewer elements is earlier
if (( ${#a[@]} < ${#b[@]} )); then
return 1
elif (( ${#a[@]} > ${#b[@]} )); then
return 3
fi
# Must be equal!
return 2
}
version / sort하며 종종가 있습니다 간단한 형태는 대략적인 결과를 제공하며 종종 효과가 있습니다.
sort -n
이것은 다음과 같은 알파 기호를 포함하는 버전에서 특히 유용합니다.
10.c.3
10.a.4
2.b.5
이건 어때?효과가 있는 것 같아?
checkVersion() {
subVer1=$1
subVer2=$2
[ "$subVer1" == "$subVer2" ] && echo "Version is same"
echo "Version 1 is $subVer1"
testVer1=$subVer1
echo "Test version 1 is $testVer1"
x=0
while [[ $testVer1 != "" ]]
do
((x++))
testVer1=`echo $subVer1|cut -d "." -f $x`
echo "testVer1 now is $testVer1"
testVer2=`echo $subVer2|cut -d "." -f $x`
echo "testVer2 now is $testVer2"
if [[ $testVer1 -gt $testVer2 ]]
then
echo "$ver1 is greater than $ver2"
break
elif [[ "$testVer2" -gt "$testVer1" ]]
then
echo "$ver2 is greater than $ver1"
break
fi
echo "This is the sub verion for first value $testVer1"
echo "This is the sub verion for second value $testVer2"
done
}
ver1=$1
ver2=$2
checkVersion "$ver1" "$ver2"
외부 콜이 없는 다른 순수 bash 솔루션을 다음에 나타냅니다.
#!/bin/bash
function version_compare {
IFS='.' read -ra ver1 <<< "$1"
IFS='.' read -ra ver2 <<< "$2"
[[ ${#ver1[@]} -gt ${#ver2[@]} ]] && till=${#ver1[@]} || till=${#ver2[@]}
for ((i=0; i<${till}; i++)); do
local num1; local num2;
[[ -z ${ver1[i]} ]] && num1=0 || num1=${ver1[i]}
[[ -z ${ver2[i]} ]] && num2=0 || num2=${ver2[i]}
if [[ $num1 -gt $num2 ]]; then
echo ">"; return 0
elif
[[ $num1 -lt $num2 ]]; then
echo "<"; return 0
fi
done
echo "="; return 0
}
echo "${1} $(version_compare "${1}" "${2}") ${2}"
또한 문제의 버전에 첫 번째 점 뒤에 선행 0이 없는 것이 확실하다면 더 간단한 해결책이 있습니다.
#!/bin/bash
function version_compare {
local ver1=${1//.}
local ver2=${2//.}
if [[ $ver1 -gt $ver2 ]]; then
echo ">"; return 0
elif
[[ $ver1 -lt $ver2 ]]; then
echo "<"; return 0
fi
echo "="; return 0
}
echo "${1} $(version_compare "${1}" "${2}") ${2}"
이것은 1.2.3 대 1.3.1 대 0.9.7과 같은 경우에는 동작하지만 1.2.3 대 1.2.3.0 또는 1.01.1 대 1.1.1에서는 동작하지 않습니다.
나는 또 다른 비교기 기능을 구현했다. 문제에는 두 요건이 (는 이 (i) 이 기능을 사용할 수 있습니다. (i) 이 기능은 기능하지 않습니다.return 1echo대신 (ii) git 저장소 버전 "1.0"은 "1.0.2"보다 커야 합니다. 즉, "1.0"은 트렁크에서 온 것입니다.
function version_compare {
IFS="." read -a v_a <<< "$1"
IFS="." read -a v_b <<< "$2"
while [[ -n "$v_a" || -n "$v_b" ]]; do
[[ -z "$v_a" || "$v_a" -gt "$v_b" ]] && echo 1 && return
[[ -z "$v_b" || "$v_b" -gt "$v_a" ]] && echo -1 && return
v_a=("${v_a[@]:1}")
v_b=("${v_b[@]:1}")
done
echo 0
}
자유롭게 코멘트와 개선을 제안해 주십시오.
버전 CLI를 사용하여 버전 제약 조건을 확인할 수 있습니다.
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Bash 스크립트의 예:
#!/bin/bash
if `version -b ">=9.0.0" "$(gcc --version)"`; then
echo "gcc version satisfies constraints >=9.0.0"
else
echo "gcc version doesn't satisfies constraints >=9.0.0"
fi
와... 이건 오래된 질문의 목록에는 한참 못 미치지만 꽤 우아한 대답인 것 같아요먼저 셸 파라미터 확장을 사용하여 도트로 구분된 각 버전을 자체 어레이로 변환합니다(쉘 파라미터 확장 참조).
v1="05.2.3" # some evil examples that work here
v2="7.001.0.0"
declare -a v1_array=(${v1//./ })
declare -a v2_array=(${v2//./ })
2개의 어레이에는 버전 번호가 priority 순으로 숫자 문자열로 지정되었습니다.위의 솔루션 중 많은 부분이 여기서부터 시작되지만 버전 문자열은 모두 임의의 기본을 가진 정수일 뿐이라는 관찰에서 비롯됩니다.스트링 내의 문자에 대해 strcmp가 하는 것과 같이 첫 번째 등가 자리수를 찾는 것을 테스트할 수 있습니다.
compare_version() {
declare -a v1_array=(${1//./ })
declare -a v2_array=(${2//./ })
while [[ -nz $v1_array ]] || [[ -nz $v2_array ]]; do
let v1_val=${v1_array:-0} # this will remove any leading zeros
let v2_val=${v2_array:-0}
let result=$((v1_val-v2_val))
if (( result != 0 )); then
echo $result
return
fi
v1_array=("${v1_array[@]:1}") # trim off the first "digit". it doesn't help
v2_array=("${v2_array[@]:1}")
done
# if we get here, both the arrays are empty and neither has been numerically
# different, which is equivalent to the two versions being equal
echo 0
return
}
첫 번째 버전이 두 번째 버전보다 작을 경우 음수, 같은 경우 0, 첫 번째 버전이 더 클 경우 양수입니다.일부 출력:
$ compare_version 1 1.2
-2
$ compare_version "05.1.3" "5.001.03.0.0.0.1"
-1
$ compare_version "05.1.3" "5.001.03.0.0.0"
0
$ compare_version "05.1.3" "5.001.03.0"
0
$ compare_version "05.1.3" "5.001.30.0"
-27
$ compare_version "05.2.3" "7.001.0.0"
-2
$ compare_version "05.1.3" "5.001.30.0"
-27
$ compare_version "7.001.0.0" "05.1.3"
2
".2" 또는 "3.0"과 같은 저하된 대소문자는 작동하지 않습니다(정의되지 않은 결과). 숫자가 아닌 문자가 '.' 옆에 있으면 실패할 수 있지만(테스트되지 않음). 확실히 정의되지 않습니다.따라서 이 기능은 삭제 기능과 짝을 이루거나 올바른 포맷을 위해 적절한 검사를 수행해야 합니다.또한, 약간의 수정이 있으면, 너무 많은 추가 수하물 없이 보다 견고하게 만들 수 있을 것이라고 확신합니다.
ver_cmp()
{
local IFS=.
local V1=($1) V2=($2) I
for ((I=0 ; I<${#V1[*]} || I<${#V2[*]} ; I++)) ; do
[[ ${V1[$I]:-0} -lt ${V2[$I]:-0} ]] && echo -1 && return
[[ ${V1[$I]:-0} -gt ${V2[$I]:-0} ]] && echo 1 && return
done
echo 0
}
ver_eq()
{
[[ $(ver_cmp "$1" "$2") -eq 0 ]]
}
ver_lt()
{
[[ $(ver_cmp "$1" "$2") -eq -1 ]]
}
ver_gt()
{
[[ $(ver_cmp "$1" "$2") -eq 1 ]]
}
ver_le()
{
[[ ! $(ver_cmp "$1" "$2") -eq 1 ]]
}
ver_ge()
{
[[ ! $(ver_cmp "$1" "$2") -eq -1 ]]
}
테스트 방법:
( ( while read V1 V2 ; do echo $V1 $(ver_cmp $V1 $V2) $V2 ; done ) <<EOF
1.2.3 2.2.3
2.2.3 2.2.2
3.10 3.2
2.2 2.2.1
3.1 3.1.0
EOF
) | sed 's/ -1 / < / ; s/ 0 / = / ; s/ 1 / > /' | column -t
1.2.3 < 2.2.3
2.2.3 > 2.2.2
3.10 > 3.2
2.2 < 2.2.1
3.1 = 3.1.0
ver_lt 10.1.2 10.1.20 && echo 'Your version is too old'
Your version is too old
배쉬가 너무 복잡해지면 파이프로 파이프로 파이핑해!
vercomp(){ echo "$1" "$2" | python3 -c "import re, sys; arr = lambda x: list(map(int, re.split('[^0-9]+', x))); x, y = map(arr, sys.stdin.read().split()); exit(not x >= y)"; }
두 버전 번호 비교 예:
vercomp 2.8 2.4.5 && echo ">=" || echo "<"
이 python one 라이너는 왼쪽 버전 번호와 오른쪽 버전 번호를 비교하고 왼쪽 버전이 같거나 그 이상인 경우 0을 종료합니다. ''와 같은 합니다.2.4.5rc3
분석 결과, 판독 가능한 코드는 다음과 같습니다.
import re, sys
# Convert a version string into a list "2.4.5" -> [2, 4, 5]
arr = lambda x: list(map(int, re.split('[^0-9]+', x)))
# Read the version numbers from stdin and apply the above function to them
x, y = map(arr, sys.stdin.read().split())
# Exit 0 if the left number is greater than the right
exit(not x >= y)
저는 이 문제를 해결했습니다.추가적인 (짧고 간단한) 답변을 추가하기 위해서...
첫 번째 주의: 이미 알고 있듯이 확장 셸 비교에 실패했습니다.
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
sort - t' .-g (또는 kanaka에서 언급한 sort - V)를 사용하여 버전을 주문하고 간단한 bash 문자열 비교를 통해 해결책을 찾았습니다.입력 파일의 3열과 4열에 비교하려는 버전이 포함되어 있습니다.일치하는지 또는 어느 쪽이 다른 쪽보다 큰지를 식별하는 목록을 반복합니다.이것이 가능한 한 간단하게 bash를 사용하여 이 작업을 수행하는 데 도움이 되기를 바랍니다.
while read l
do
#Field 3 contains version on left to compare (change -f3 to required column).
kf=$(echo $l | cut -d ' ' -f3)
#Field 4 contains version on right to compare (change -f4 to required column).
mp=$(echo $l | cut -d ' ' -f4)
echo 'kf = '$kf
echo 'mp = '$mp
#To compare versions m.m.m the two can be listed and sorted with a . separator and the greater version found.
gv=$(echo -e $kf'\n'$mp | sort -t'.' -g | tail -n 1)
if [ $kf = $mp ]; then
echo 'Match Found: '$l
elif [ $kf = $gv ]; then
echo 'Karaf feature file version is greater '$l
elif [ $mp = $gv ]; then
echo 'Maven pom file version is greater '$l
else
echo 'Comparison error '$l
fi
done < features_and_pom_versions.tmp.txt
배리의 블로그 덕분에...참조: http://bkhome.org/blog/?viewDetailed=02199
### the answer is does we second argument is higher
function _ver_higher {
ver=`echo -ne "$1\n$2" |sort -Vr |head -n1`
if [ "$2" == "$1" ]; then
return 1
elif [ "$2" == "$ver" ]; then
return 0
else
return 1
fi
}
if _ver_higher $1 $2; then
echo higher
else
echo same or less
fi
그것은 꽤 간단하고 작다.
Dennis의 솔루션 덕분에 비교 연산자 '>', '<', '=', '=', '<=', '>='로 확장할 수 있습니다.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
그런 다음 다음과 같은 식으로 비교 연산자를 사용할 수 있습니다.
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
다음과 같이 결과의 참/거짓만 테스트합니다.
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
여기 또 다른 순수한 bash 버전이 있습니다.허용되는 답변보다 작습니다.버전이 "최소 버전"보다 작거나 동일한지 여부만 체크하고 사전 편집 방식으로 영숫자 시퀀스를 체크합니다.이 경우 잘못된 결과가 자주 나타납니다(일반적인 예를 들어 스냅샷은 "릴리스"보다 늦지는 않습니다).메이저/마이너는 정상적으로 동작합니다.
is_number() {
case "$BASH_VERSION" in
3.1.*)
PATTERN='\^\[0-9\]+\$'
;;
*)
PATTERN='^[0-9]+$'
;;
esac
[[ "$1" =~ $PATTERN ]]
}
min_version() {
if [[ $# != 2 ]]
then
echo "Usage: min_version current minimum"
return
fi
A="${1%%.*}"
B="${2%%.*}"
if [[ "$A" != "$1" && "$B" != "$2" && "$A" == "$B" ]]
then
min_version "${1#*.}" "${2#*.}"
else
if is_number "$A" && is_number "$B"
then
[[ "$A" -ge "$B" ]]
else
[[ ! "$A" < "$B" ]]
fi
fi
}
언급URL : https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash
'programing' 카테고리의 다른 글
| XAML에서 변수를 정의하고 사용하여 색상을 정의하려면 어떻게 해야 합니까? (0) | 2023.04.12 |
|---|---|
| 사전에서 키-값 쌍의 하위 집합을 추출하시겠습니까? (0) | 2023.04.12 |
| WPF 어플리케이션에서 콘솔로 출력되지 않았습니까? (0) | 2023.04.12 |
| 자동 덮어쓰기와 함께 workbook.saveas를 사용하는 방법 (0) | 2023.04.12 |
| init coder a Decoder란 정확히 무엇입니까? (0) | 2023.04.12 |