답변:
다음은 외부 유틸리티가 필요없는 순수한 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'
Please don't use it for software or documentation, since it is incompatible with the GNU GPL
: /하지만 좋은 코드 1
coreutils-7 (Ubuntu Karmic에는 있지만 Jaunty는 아님) sort
이있는 경우 명령 -V
에 비교를 수행하는 데 사용할 수 있는 옵션 (버전 정렬)이 있어야합니다.
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
brew install coreutils
. 그런 다음 gsort를 사용하도록 위의 내용을 수정해야합니다.
sort
에는 -V
옵션 이 없으므로 내장 Linux 시스템에서 Busybox와 함께 작동 하지 않습니다 .
printf
대신 사용하는 것이 좋습니다 echo -e
.
sort
는 또한 -C
또는 을 가지고 --check=silent
있으므로 쓸 수 있습니다 verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
. 보다 간단하게 수행하는 것보다 엄격하게 검사 verlt() { ! verlte "$2" "$1" }
합니다.
이를 달성하기위한 보편적으로 올바른 방법은 없을 것입니다. 데비안 패키지 시스템에서 버전을 비교하려는 경우dpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
1.0이 1.2 미만임을 의미합니다. 비교 결과 $?
는 0
true이면 바로 after if
문을 사용할 수 있습니다 .
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
에는 -V
옵션 이 없으므로 내장 Linux 시스템에서 Busybox와 함께 작동 하지 않습니다 .
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
-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
-t
옵션은 단일 문자 탭만 허용합니다. 그렇지 않으면 2.4-r9
작동합니다. 부끄러운 일 : /
-g
해야했습니다 -n
. 이 예제를 사용하지 않는 이유는 무엇입니까? 부수적으로 ... "보다 큼"유형 비교를 수행하기 위해 원하는 정렬이 실제 정렬 desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
과 같은지 확인한 다음 ( 예 : 확인) 확인할 수 if [ "$desired" = "$actual" ]
있습니다.
버전에서 최대 4 개의 필드입니다.
$ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
$ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello
hello
printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' '\n' | head -n 4)
head -n
일하기 위해, 나는 다음과 같이 바꾸어야했다tr '.' '\n'
tr
출력을 통해 sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
처리합니다 (오히려)
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
여기.
에서 가져온 다음 알고리즘과 같이 재귀 적으로 분할 하고 비교할 수 있습니다 . 버전이 동일한 경우 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"
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>
V
-순수 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
- 비교 연산자 및 피연산자, 즉, "3.6"> "3.5A".al
, bl
- a
및의 문자 꼬리가 b
꼬리 항목으로 초기화됩니다 (예 : "6"및 "5a").2, 3 행 : 꼬리 항목의 왼쪽 자르기 숫자이므로 ""및 "a"와 같이 문자 만 남습니다.
행 4 : 오른쪽 트림 편지에서 a
와 b
지역 변수로 숫자 항목의 바로 시퀀스를두고하는 ai
과를 bi
, 즉, "3.6"및 "3.5". 주목할만한 예 : "4.01-RC2"> "4.01-RC1"은 ai = "4.01"al = "-RC2"및 bi = "4.01"bl = "-RC1"을 생성합니다.
6 행 : 지역 변수를 정의합니다.
ap
, bp
- ai
및에 대한 오른쪽 패딩이 없습니다 bi
. 항목 간 점만 유지하여 시작하십시오.이 수는 각각 a
및 의 요소 수와 같습니다 b
.7 행 : 그런 다음 각 점 뒤에 "0"을 추가하여 패딩 마스크를 만듭니다.
9 행 : 지역 변수 :
w
-항목 너비fmt
-계산할 printf 형식 문자열x
-일시적IFS=.
bash를 사용하면 변수 값이 '.'로 분할됩니다.10 행 : w
최대 항목 너비를 계산 합니다.이 항목은 사전 비교를 위해 항목을 정렬하는 데 사용됩니다. 이 예에서는 w = 2입니다.
행 11 :의 각 문자를 대체하여 printf와 정렬 형식을 만들기 $a.$b
로 %${w}s
즉, "3.6"> "3.5A"수율 "% 2 초 % 2 초 % 2 초 % 2 초".
12 행 : "printf -v a"는 variable 값을 설정합니다 a
. 이것은 a=sprintf(...)
많은 프로그래밍 언어 와 동일합니다 . 여기서는 IFS =의 영향을받습니다. printf
개별 항목 으로 분할 하는 인수
첫 번째 printf
항목에는 a
공백이 왼쪽으로 채워 bp
지고 결과 문자열 a
을 유사한 형식과 의미있게 비교할 수 있도록 충분한 "0"항목이 추가됩니다 b
.
우리가 추가합니다 bp
-하지 ap
에 ai
있기 때문에 ap
하고 bp
, 다른 거리에서는 쉴드를 가질 수있다이 결과 지금 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
BusyBox와 함께 임베디드 Linux (Yocto)를 사용하고 있습니다. BusyBoxsort
에는 -V
옵션이 없지만 BusyBoxexpr match
는 정규 표현식을 수행 할 수 있습니다. 그래서 나는 그 제약 조건에 맞는 Bash 버전 비교가 필요했습니다.
"자연적인 정렬"유형의 알고리즘을 사용하여 비교 하기 위해 다음과 같이했습니다 ( Dennis Williamson의 답변 과 유사 ). 문자열을 숫자 부분과 숫자가 아닌 부분으로 나눕니다. 숫자 부분 (숫자 10
보다 큼 9
)을 비교하고 숫자가 아닌 부분을 일반 ASCII 비교로 비교합니다.
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-r3
대 1.2-r4
1.2rc3
대 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 <
그러나 이것은 모퉁이의 경우이며 결과는 여전히 타당하다고 생각합니다.
$ 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
--check=silent
하면 다음과 test
같이 필요 if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
pure bash
printf는 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//./ }
}
이건 어때요? 작동하는 것 같습니까?
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 vs 1.3.1 vs 0.9.7과 같이 작동하지만 1.2.3 vs 1.2.3.0 또는 1.01.1 vs 1.1.1에서는 작동하지 않습니다.
4.4.4 > 44.3
다음은 더 간결하고 다른 반환 값 체계를 사용하여 단일 비교로 <= 및> =를 쉽게 구현할 수있는 상위 답변 (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
}
또 다른 비교기 기능을 구현했습니다. 이 사람은 두 가지 특정 요구 사항을했다 : (내가) 내가 함수를 사용하여 실패하고 싶지 않았다 return 1
하지만 echo
대신에; (ii) git 저장소 버전 "1.0"에서 버전을 검색 할 때 "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
}
의견을 말하고 개선을 제안하십시오.
나는 (더 짧고 간단한) 추가 답변을 추가하기 위해이 문제를 해결했습니다.
먼저, 이미 알고 있듯이 확장 쉘 비교가 실패했습니다 ...
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
정렬 아이디어에 대한 Barry의 블로그 덕분에 ... 참고 : 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
아주 간단하고 작습니다.
echo -ne "$1\n$2"
하는 것이 좋습니다 printf '%s\n ' "$1" "$2"
. 또한 $()
백틱 대신 사용 하는 것이 좋습니다.
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
}
질문에 따라 점으로 구분 된 버전을 비교하는 다른 접근 방식 (@joynes의 수정 된 버전)
(예 : "1.2", "2.3.4", "1.0", "1.10.1"등).
최대 위치 수를 미리 알고 있어야합니다. 이 접근법은 최대 3 개의 버전 위치를 예상합니다.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
사용법 예 :
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
반환 : 1.10.1 이후 1은 1.7보다 큽니다.
expr $(printf "1.10.1\n1.11" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.11"
1.10.1이 1.11보다 낮으므로 0
다음 은 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
}
# 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
echo -e "\nThe following line should be empty (local variables test):"
echo "$op $regex $va1 $vr1 $va2 $vr2 $len $i $IFS"
와우 .. 이것은 오래된 질문의 목록에 있지만, 이것은 꽤 우아한 대답이라고 생각합니다. 먼저 쉘 매개 변수 확장을 사용하여 점으로 구분 된 각 버전을 자체 배열로 변환하십시오 ( 쉘 매개 변수 확장 참조 ).
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//./ })
이제 두 배열의 버전 번호가 우선 순위에 따라 숫자 문자열로 표시됩니다. 위의 많은 솔루션이 거기에서 가져 오지만 버전 문자열은 임의의 기준을 가진 정수라는 관찰에서 파생됩니다. 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"과 같은 사례는 퇴보합니다. 작동하지 않으며 (정의되지 않은 결과) 숫자가 아닌 문자가 '.'옆에 있으면 실패했을 수도 있지만 테스트되지 않았을 수도 있습니다. 따라서 이것은 살균 기능 또는 올바른 형식에 대한 적절한 검사와 쌍을 이루어야합니다. 또한, 약간의 조정으로 확신 할 수 있습니다. 수하물을 너무 많이 넣지 않고도 더욱 견고해질 수 있습니다.
function version_compare () {
function sub_ver () {
local len=${#1}
temp=${1%%"."*} && indexOf=`echo ${1%%"."*} | echo ${#temp}`
echo -e "${1:0:indexOf}"
}
function cut_dot () {
local offset=${#1}
local length=${#2}
echo -e "${2:((++offset)):length}"
}
if [ -z "$1" ] || [ -z "$2" ]; then
echo "=" && exit 0
fi
local v1=`echo -e "${1}" | tr -d '[[:space:]]'`
local v2=`echo -e "${2}" | tr -d '[[:space:]]'`
local v1_sub=`sub_ver $v1`
local v2_sub=`sub_ver $v2`
if (( v1_sub > v2_sub )); then
echo ">"
elif (( v1_sub < v2_sub )); then
echo "<"
else
version_compare `cut_dot $v1_sub $v1` `cut_dot $v2_sub $v2`
fi
}
### Usage:
version_compare "1.2.3" "1.2.4"
# Output: <
크레딧은 @Shellman으로 갑니다
bc
. 숫자가 아닌 텍스트입니다.2.1 < 2.10
이 방법으로 실패합니다.