2010-08-18 30 views
8

Định dạng của các phiên bản - X.X.X.X.
Trong đó X - số.
Cách tốt nhất để so sánh hai phiên bản là gì?
tôi sử dụng đoạn mã sau:Làm thế nào để so sánh các phiên bản của một số sản phẩm trong vỏ unix ksh?

compareVersions() 
{ 
    VER_1=$1 
    VER_2=$2 

    print -R "$VER_1"| IFS=. read v1_1 v1_2 v1_3 v1_4 
    print -R "$VER_2"| IFS=. read v2_1 v2_2 v2_3 v2_4 

    RESULT="0" 

    if [[ "${v1_1}" -lt "${v2_1}" ]] 
    then 
    RESULT="-1" 
    elif [[ "${v1_1}" -gt "${v2_1}" ]] 
    then 
    RESULT="1" 
    elif [[ "${v1_2}" -lt "${v2_2}" ]] 
    then 
    RESULT="-1" 
    elif [[ "${v1_2}" -gt "${v2_2}" ]] 
    then 
    RESULT="1" 
    elif [[ "${v1_3}" -lt "${v2_3}" ]] 
    then 
    RESULT="-1" 
    elif [[ "${v1_3}" -gt "${v2_3}" ]] 
    then 
    RESULT="1" 
    elif [[ "${v1_4}" -lt "${v2_4}" ]] 
    then 
    RESULT="-1" 
    elif [[ "${v1_4}" -gt "${v2_4}" ]] 
    then 
    RESULT="1" 
    fi 

    echo "$RESULT" 
} 

Nhưng tôi không thích nó - đó là rất đơn giản.
Có thể có nhiều cách chính xác để so sánh các phiên bản?

+1

Bạn có chắc chắn đó không phải là ksh thay vì Bash? Bash không có lệnh 'print' và bạn không thể đi vào' read' của nó. –

+0

Có - đây là ksh. Tôi đã sửa thẻ. –

+2

"bạn không thể đọc được đường dẫn" - tất nhiên bạn có thể. 'printf" abc \ n "| {đọc x; printf "có $ x \ n"; } ' –

Trả lời

11

tinh khiết Bash/ksh:

compareVersions() 
{ 
    typeset IFS='.' 
    typeset -a v1=($1) 
    typeset -a v2=($2) 
    typeset n diff 

    for ((n=0; n<4; n+=1)); do 
    diff=$((v1[n]-v2[n])) 
    if [ $diff -ne 0 ] ; then 
     [ $diff -le 0 ] && echo '-1' || echo '1' 
     return 
    fi 
    done 
    echo '0' 
} # ---------- end of function compareVersions ---------- 
+1

+1 Bạn nên tạo tất cả các biến cục bộ. –

+0

Bạn nói đúng. Chỉ cần cố định. –

+0

fgm cảm ơn kịch bản!Chỉ vì tôi là một người đam mê và có nhu cầu làm cho mọi thứ chính xác "$ diff -le 0" nên là "$ diff -lt 0". Vì $ diff sẽ không bao giờ là 0 vào thời điểm này vì điều kiện trước đó. – ptsw

7

Có thể bạn có thể sử dụng awk?

echo $VER_1 $VER2 | \ 
awk '{ split($1, a, "."); 
     split($2, b, "."); 
     for (i = 1; i <= 4; i++) 
      if (a[i] < b[i]) { 
       x =-1; 
       break; 
      } else if (a[i] > b[i]) { 
       x = 1; 
       break; 
      } 
     print x; 
    }' 

Không có cách nào hoàn hảo để thực hiện việc này. Như được hiển thị, bạn có thể sử dụng mảng/vòng lặp cho các số, cũng trong bash.

+1

Tôi nghĩ đây là phương pháp ưa thích của tôi về tính tương thích, vì' sắp xếp-V' là không ' Hỗ trợ rộng rãi đủ, và sử dụng 'awk' tránh sự cần thiết cho các tính năng tích hợp cụ thể. – Haravikk

+1

Bạn có thể thực hiện việc này ngắn hơn một chút bằng cách thay đổi vòng lặp 'for' thành' cho (i = 1;! X && i <= 4; ++ i) x = (a [i] b [i])? 1: 0); ' – ldav1s

2

Nếu bạn có thể gian lận bằng cách sử dụng Perl trong shell script của bạn, hãy thử nó built-in handling of version strings với những hoạt động chuỗi so sánh:

V1=1.1.3; V2=1.1 
echo $(perl -e '($x,$y)[email protected]; print $x cmp $y' $V1 $V2) 

Bạn cũng có thể làm với các biến Perl và chỉ sử dụng thay đổi:

result=$(perl -e 'print shift cmp shift' $V1 $V2) 

Nhưng điều đó không thành công trên v ersions> 10. Vì vậy, bạn có thể thử này để thay thế:

perl -e '($a,$b)[email protected]; for ($a,$b) {s/(\d+)/sprintf "%5d", $1/ge}; print $a cmp $b;' 12.1.3 9.0.2 

Các sprintf của "% 5d" là để chắc chắn rằng nó thậm chí sẽ làm việc cho Firefox, cho đến khi phiên bản 99999 ... :-)

Rõ ràng , bạn cũng có thể sử dụng các toán tử chuỗi Perl khác như gt, lt, ge và le.

4

Bạn có thể sử dụng sort -V để sắp xếp các dòng với các phiên bản và phù hợp với phiên bản của bạn chống lại kết quả:

% cat sorttest 
#!/bin/sh 

version_lt() { 
    echo "$1\n$2" | sort -V | head -n 1 | grep -q "$1" 
} 

display_versioncmp() { 
    version_lt "$1" "$2" && echo "$1 < $2" || echo "$1 > $2" 
} 

X="1.2.3" 
Y="11.2.3" 
Z="1.22.3" 

display_versioncmp "$X" "$Y" 
display_versioncmp "$Y" "$X" 
display_versioncmp "$X" "$Z" 
display_versioncmp "$Z" "$X" 
display_versioncmp "$Z" "$Y" 
display_versioncmp "$Y" "$Z" 

% ./sorttest 
1.2.3 < 11.2.3 
11.2.3 > 1.2.3 
1.2.3 < 1.22.3 
1.22.3 > 1.2.3 
1.22.3 < 11.2.3 
11.2.3 > 1.22.3 
+2

Thật không may, không phải mọi bash của nền tảng đều có 'loại -V'. (Ví dụ: Mac có thể không.) – kcrisman

+0

Tập lệnh không chính xác. Nó không giải quyết tất cả các trường hợp như 3.22.3> 11.2.3 nếu đầu vào là 3 và 11 thì chắc chắn 11 là phiên bản cao hơn nhưng kịch bản cho thấy 3 có phiên bản cao hơn. –

+1

@AnandChoubey bạn có loại phiên bản nào? Với của tôi (đó là Ubuntu 12.04) tất cả các công trình như mong đợi: 3.22.3> 1.2.3 3.22.3 <11.2.3 # sort --version sắp xếp (GNU coreutils) 8.13 – timurb

0

Tôi có vấn đề này, và sau khi giải quyết nó trông để xem nếu có là một câu trả lời tốt hơn đã có sẵn. Phiên bản của tôi cho phép so sánh các chuỗi phiên bản độ dài khác nhau và là hàm version_ge() bên dưới, được sử dụng làm toán tử "lớn hơn hoặc bằng", như trong

nếu version_ge "$ version" "1.2.3.4" ; sau đó ...

#!/bin/sh 

# Usage: split "<word list>" <variable1> <variable2>... 
# Split a string of $IFS seperated words into individual words, and 
# assign them to a list of variables. If there are more words than 
# variables then all the remaining words are put in the last variable; 
# use a dummy last variable to collect any unwanted words. 
# Any variables for which there are no words are cleared. 
# eg. split 'hello Fred this is Bill' greeting who extra 
# sets greeting=hello who=Fred extra="this is Bill" 
# and split "$list" word list # "pops" the first word from a list 
split() 
{ 
    # Prefix local names with the function name to try to avoid conflicts 
    # local split_wordlist 
    split_wordlist="$1" 
    shift 
    read "[email protected]" <<EOF-split-end-of-arguments 
${split_wordlist} 
EOF-split-end-of-arguments 
} 


# Usage: version_ge v1 v2 
# Where v1 and v2 are multi-part version numbers such as 12.5.67 
# Missing .<number>s on the end of a version are treated as .0, & leading 
# zeros are not significant, so 1.2 == 1.2.0 == 1.2.0.0 == 01.2 == 1.02 
# Returns true if v1 >= v2, false if v1 < v2 
version_ge() 
{ 
    # Prefix local names with the function name to try to avoid conflicts 
    # local version_ge_1 version_ge_2 version_ge_a version_ge_b 
    # local version_ge_save_ifs 
    version_ge_v1="$1" 
    version_ge_v2="$2" 

    version_ge_save_ifs="$IFS" 
    while test -n "${version_ge_v1}${version_ge_v2}"; do 
     IFS="." 
     split "$version_ge_v1" version_ge_a version_ge_v1 
     split "$version_ge_v2" version_ge_b version_ge_v2 
     IFS="$version_ge_save_ifs" 
     #echo " compare $version_ge_a $version_ge_b" 
     test "0$version_ge_a" -gt "0$version_ge_b" && return 0 # v1>v2: true 
     test "0$version_ge_a" -lt "0$version_ge_b" && return 1 # v1<v2:false 
    done 
    # version strings are both empty & no differences found - must be equal. 
    return 0 # v1==v2: true 
} 
Các vấn đề liên quan