คำตอบ:
นี่คือเวอร์ชั่นทุบตีบริสุทธิ์ที่ไม่ต้องการยูทิลิตี้ภายนอก
#!/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
ตัวเลือก (version sort) ซึ่งคุณสามารถใช้ทำการเปรียบเทียบ:
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
homebrew: จากนั้นควรแก้ไขข้างต้นเพื่อใช้ gsort
sort
ไม่มี-V
ตัวเลือก
printf
echo -e
sort
ยังมี-C
หรือ--check=silent
ดังนั้นคุณสามารถเขียนverlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; verlt() { ! verlte "$2" "$1" }
และการตรวจสอบที่เข้มงวดน้อยกว่าจะทำมากขึ้นก็เป็น
อาจไม่มีวิธีที่ถูกต้องในระดับสากลเพื่อให้บรรลุสิ่งนี้ หากคุณพยายามเปรียบเทียบรุ่นในระบบแพ็คเกจ Debian ลองdpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
หมายถึง 1.0 น้อยกว่า 1.2 ผลลัพธ์การเปรียบเทียบ$?
คือ0
ถ้าเป็นจริงเพื่อให้คุณสามารถใช้มันได้โดยตรงหลังif
คำสั่ง
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
ไม่มี-V
ตัวเลือก
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 ถ้ารุ่นเหมือนกัน 11 ถ้าเป็นรุ่น 1 มากกว่ารุ่น 2 และ 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"
หากเป็นเพียงการรู้ว่ารุ่นหนึ่งต่ำกว่าอีกรุ่นฉันมาตรวจสอบว่าsort --version-sort
เปลี่ยนลำดับของสตริงรุ่นของฉัน:
string="$1
$2"
[ "$string" == "$(sort --version-sort <<< "$string")" ]
ฉันใช้งานฟังก์ชั่นที่ส่งกลับผลลัพธ์เดียวกับ 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 ก็ใช้ได้เช่นกัน มันสามารถขยายได้อย่างง่ายดายมากขึ้น จะนำไปปฏิบัติ=
, <
,<=
, >
, >=
และ!=
เงื่อนไข
#!/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
- โซลูชั่นทุบตีบริสุทธิ์โดยไม่ต้องใช้ยูทิลิตี้ภายนอก=
==
!=
<
<=
>
และ>=
(พจนานุกรม)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
- ศูนย์ขวา paddings สำหรับและai
bi
เริ่มต้นด้วยการรักษาจุดระหว่างรายการเท่านั้นซึ่งจำนวนนั้นเท่ากับจำนวนองค์ประกอบของa
และb
ตามลำดับบรรทัดที่ 7 : จากนั้นผนวก "0" หลังจากแต่ละจุดเพื่อสร้างมาสก์ padding
บรรทัดที่ 9 : ตัวแปรท้องถิ่น:
w
- ความกว้างของรายการfmt
- สตริงรูปแบบ printf ที่ต้องคำนวณx
- ชั่วคราวIFS=.
bash แยกค่าตัวแปรที่ '.'บรรทัดที่ 10 : คำนวณw
ความกว้างของรายการสูงสุดซึ่งจะใช้เพื่อจัดเรียงรายการสำหรับการเปรียบเทียบพจนานุกรม ในตัวอย่างของเรา w = 2
บรรทัดที่ 11 : สร้างรูปแบบการจัดตำแหน่ง printf โดยแทนที่อักขระแต่ละตัว$a.$b
ด้วย%${w}s
เช่น "3.6"> "3.5a" ให้ผลตอบแทน "% 2s% 2s% 2s% 2s% 2s"
บรรทัดที่ 12 : "printf -v" a
การตั้งค่าของตัวแปร สิ่งนี้เทียบเท่ากับa=sprintf(...)
ภาษาการเขียนโปรแกรมหลายภาษา โปรดทราบว่าที่นี่โดยผลกระทบของ IFS = ข้อโต้แย้งที่จะprintf
แบ่งออกเป็นแต่ละรายการ
ด้วยprintf
รายการแรกของa
ถูกเพิ่มด้วยช่องว่างในขณะที่รายการ "0" เพียงพอที่จะผนวกเข้าด้วยกันbp
เพื่อให้แน่ใจว่าสตริงผลลัพธ์a
สามารถถูกเปรียบเทียบอย่างมีความหมายกับรูปแบบที่คล้ายกันb
สามารถมีความหมายเมื่อเทียบกับการจัดรูปแบบในทำนองเดียวกัน
โปรดทราบว่าเราผนวกbp
- ไม่ได้ap
ไปai
เพราะap
และbp
อาจมี lenghts แตกต่างกันดังนั้นผลการนี้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
ฉันใช้ Linux (Yocto) ในตัวกับ BusyBox BusyBoxsort
ไม่มี-V
ตัวเลือก (แต่BusyBoxexpr match
สามารถทำนิพจน์ทั่วไปได้) ดังนั้นฉันต้องการรุ่น Bash เปรียบเทียบซึ่งทำงานกับข้อ จำกัด นั้น
ฉันได้ทำสิ่งต่อไปนี้ (คล้ายกับคำตอบของเดนนิสวิลเลียมสัน ) เพื่อเปรียบเทียบโดยใช้อัลกอริทึมแบบ "เรียงลำดับธรรมชาติ" มันแยกสตริงออกเป็นส่วนที่เป็นตัวเลขและชิ้นส่วนที่ไม่ใช่ตัวเลข มันเปรียบเทียบส่วนที่เป็นตัวเลข (ดังนั้น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
หมายเหตุว่ามันไม่ได้กลับผลเดียวกันสำหรับบางส่วนของมุมในกรณีที่คำตอบที่เดนนิสวิลเลียมสัน โดยเฉพาะอย่างยิ่ง:
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 builtin
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//./ }
}
สำหรับรุ่นเก่า / 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"
นี่เป็นอีกวิธีการทุบตีบริสุทธิ์โดยไม่ต้องโทรภายนอก:
#!/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}"
และยังมีวิธีแก้ปัญหาที่ง่ายยิ่งขึ้นถ้าคุณแน่ใจว่ารุ่นที่เป็นปัญหานั้นไม่มีเลขศูนย์นำหน้าหลังจากจุดแรก:
#!/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
4.4.4 > 44.3
นี่คือการปรับแต่งของคำตอบยอดนิยม (เดนนิส) ที่มีความรัดกุมมากกว่าและใช้รูปแบบค่าตอบแทนที่แตกต่างกันเพื่อให้ง่ายต่อการใช้ <= และ> = ด้วยการเปรียบเทียบเพียงครั้งเดียว นอกจากนี้ยังเปรียบเทียบทุกอย่างหลังจากอักขระตัวแรกที่ไม่ใช่ใน [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
}
ฉันใช้งานฟังก์ชั่นเปรียบเทียบอื่น อันนี้มีข้อกำหนดเฉพาะสองประการ: (i) ฉันไม่ต้องการให้ฟังก์ชันล้มเหลวโดยใช้return 1
แต่ใช้echo
แทน (ii) เนื่องจากเรากำลังเรียกข้อมูลรุ่นจากที่เก็บ git รุ่น "1.0" ควรใหญ่กว่า "1.0.2" ซึ่งหมายความว่า "1.0" มาจาก trunk
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
ฉันเจอและแก้ไขปัญหานี้เพื่อเพิ่มคำตอบ (และสั้นลงและง่ายขึ้น) ...
หมายเหตุแรกการเปรียบเทียบเชลล์แบบขยายล้มเหลวเนื่องจากคุณอาจทราบแล้ว ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
ใช้ sort -t '.'- g (หรือ sort -V ตามที่กล่าวไว้โดย kanaka) เพื่อเรียงลำดับรุ่นและเปรียบเทียบสตริง 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 สำหรับแนวคิดในการจัดเรียง ... ref: 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"
นอกจากนี้ยังเป็นการดีกว่าที่จะใช้$()
แทนที่จะเป็น backtics
ขอบคุณโซลูชันของเดนนิสเราสามารถขยายได้เพื่อให้สามารถเปรียบเทียบตัวดำเนินการ '>', '<', '=', '==', '<=' และ '> ='
# 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
นี่เป็นอีกเวอร์ชั่นทุบตีบริสุทธิ์แทนที่จะเล็กกว่าคำตอบที่ยอมรับ จะตรวจสอบเฉพาะรุ่นที่น้อยกว่าหรือเท่ากับ "รุ่นขั้นต่ำ" และจะตรวจสอบลำดับตัวอักษรและตัวเลขพจนานุกรมซึ่งมักจะให้ผลลัพธ์ที่ผิด ("สแนปชอต" ไม่ช้ากว่า "รีลีส" เพื่อเป็นตัวอย่างทั่วไป) . มันจะทำงานได้ดีสำหรับรายใหญ่ / รายย่อย
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 ตั้งแต่ 1.10.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"
ผลตอบแทน: 0 ตั้งแต่ 1.10.1 ต่ำกว่า 1.11
นี่คือโซลูชัน Bash บริสุทธิ์ที่สนับสนุนการแก้ไข (เช่น '1.0-r1') ตามคำตอบที่โพสต์โดย Dennis Williamsonคำตอบโพสต์โดยเดนนิสวิลเลียมสันสามารถแก้ไขได้อย่างง่ายดายเพื่อรองรับสิ่งต่าง ๆ เช่น '-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
}
นี่จะเป็นตัวเลขลบหากเวอร์ชันแรกน้อยกว่าวินาทีซึ่งเป็นศูนย์ถ้ามันเท่ากันและเป็นจำนวนบวกถ้าเวอร์ชันแรกมากกว่า เอาท์พุทบาง:
$ 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
จะล้มเหลวด้วยวิธีนี้