2013-09-07 33 views
252

Tôi bắt đầu tìm hiểu về cách viết tập lệnh cho thiết bị đầu cuối bash, nhưng tôi không thể tìm ra cách để so sánh hoạt động đúng cách. Tôi chắc rằng điều này rất cơ bản đối với nhiều người trong số các bạn, tôi dường như không thể tìm ra câu trả lời cho câu hỏi của mình ở bất cứ đâu (hoặc ít nhất tôi không chắc chắn nên tìm kiếm gì). Các kịch bản tôi đang sử dụng là:So sánh các số trong Bash

echo "enter two numbers"; 

read a b; 

echo "a=$a"; 
echo "b=$b"; 

if [ $a \> $b ]; 
then 
    echo "a is greater than b"; 
else 
    echo "b is greater than a"; 
fi; 

Tôi có vấn đề là nó so sánh số từ chữ số đầu tiên trên, tức là 9 lớn hơn 10000, nhưng 1 là lớn hơn 09. Làm thế nào tôi có thể chuyển đổi số vào một loại để làm một so sánh thực sự?

+1

đọc cơ bản: [BashFAQ] (http : //mywiki.wooledge.org/BashFAQ) –

+1

BTW, trong bash một dấu chấm phẩy là một dấu tách câu lệnh, không phải là một dấu kết thúc câu lệnh, mà là một dòng mới. Vì vậy, nếu bạn chỉ có một câu lệnh trên một dòng thì ';' ở cuối dòng là thừa. Không làm hại gì, chỉ là một sự lãng phí các thao tác gõ phím (trừ khi bạn * thích * gõ dấu chấm phẩy). – cdarke

+3

Để buộc các số có số 0 đứng đầu thành số thập phân: '10 # $ number' để' số = 09; echo "$ ((10 # $ number))" 'sẽ xuất' 9' trong khi 'echo $ ((số))' sẽ tạo ra lỗi "giá trị quá lớn đối với cơ sở". –

Trả lời

445

Trong bash, bạn nên làm kiểm tra của bạn trong arithmetic context:

if ((a > b)); then 
    ... 
fi 

Đối với POSIX vỏ không hỗ trợ (()), bạn có thể sử dụng -lt-gt.

if [ "$a" -gt "$b" ]; then 
    ... 
fi 

Bạn có thể nhận danh sách đầy đủ các toán tử so sánh với help test.

+0

Điều đó chắc chắn làm việc nhưng tôi vẫn nhận được "((: 09: giá trị quá lớn cho cơ sở (mã thông báo lỗi là" 09 ")" nếu tôi so sánh 1 và 09 nhưng không phải 01 và 09 là kỳ quặc, nhưng về cơ bản – advert2013

+4

@ advert2013 bạn không nên thêm tiền tố bằng số 0. Số không có tiền tố là bát phân trong bash –

+0

Ah ok, giải quyết bí ẩn đó - Tôi không thực sự có bất kỳ sử dụng cho số không có tiền tố nhưng nó là tốt đẹp để biết các lỗi đến từ – advert2013

13

Trong Bash tôi thích làm điều này vì nó tự giải quyết nhiều hơn như là một hoạt động có điều kiện không giống như sử dụng (()), đó là nhiều số học.

[[ N -gt M ]] 

Trừ khi tôi làm chất liệu phức tạp như

(((N + 1) > M)) 

Nhưng tất cả mọi người chỉ có sở thích riêng của họ. Điều đáng buồn là một số người áp đặt các tiêu chuẩn không chính thức của họ.

Cập nhật:

Bạn thực sự cũng có thể làm điều này:

[[ 'N + 1' -gt M ]] 

nào cho phép bạn thêm cái gì khác mà bạn có thể làm với [[ ]] bên cạnh chất liệu số học.

+0

Điều này dường như ngụ ý rằng '[[]]' buộc một ngữ cảnh số học như '(()) ',' N' được xử lý như thể nó là '$ N', nhưng tôi không nghĩ đó là chính xác. Hoặc, nếu đó không phải là ý định, việc sử dụng' N' và 'M' là khó hiểu. –

29

Ngoài ra còn có một điều tốt đẹp một số người có thể không biết về:

echo $((a < b ? a : b)) 

Mã này sẽ in số lượng nhỏ nhất ra khỏi ab

+3

Điều đó không đúng, nó cũng sẽ in 'b' nếu' a == b'. – konsolebox

+52

@ konsolebox là nó chỉ cho tôi, hoặc số nhỏ nhất trong số 5 và 5 là 5? –

+4

Tuyên bố của bạn không rõ ràng. Thậm chí áp dụng trên một lệnh như thế này sẽ không làm: 'echo" Số nhỏ hơn là $ ((a konsolebox

4

Mã này cũng có thể so sánh nổi. Đó là sử dụng awk (nó không phải là bash tinh khiết), tuy nhiên điều này không phải là một vấn đề, như awk là một lệnh POSIX tiêu chuẩn có nhiều khả năng được vận chuyển theo mặc định với hệ điều hành của bạn.

$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}' 
$ echo $? 
0 
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}' 
$ echo $? 
0 
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}' 
$ echo $? 
1 
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}' 
$ echo $? 
0 
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}' 
$ echo $? 

Để làm cho nó ngắn hơn để sử dụng, sử dụng chức năng này:

compare_nums() 
{ 
    # Function to compare two numbers (float or integers) by using awk. 
    # The function will not print anything, but it will return 0 (if the comparison is true) or 1 
    # (if the comparison is false) exit codes, so it can be used directly in shell one liners. 
    ############# 
    ### Usage ### 
    ### Note that you have to enclose the comparison operator in quotes. 
    ############# 
    # compare_nums 1 ">" 2 # returns false 
    # compare_nums 1.23 "<=" 2 # returns true 
    # compare_nums -1.238 "<=" -2 # returns false 
    ############################################# 
    num1=$1 
    op=$2 
    num2=$3 
    E_BADARGS=65 

    # Make sure that the provided numbers are actually numbers. 
    if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi 
    if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi 

    # If you want to print the exit code as well (instead of only returning it), uncomment 
    # the awk line below and comment the uncommented one which is two lines below. 
    #awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}' 
    awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}' 
    return_code=$? 
    return $return_code 
} 

$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false 
true 
$ compare_nums -1.2345 ">=" 23 && echo true || echo false 
false 
+0

Tôi đang làm việc với số lượng lớn và 'bash' không so sánh chúng đúng cách (hãy thử' if ((18446744073692774399 <8589934592)); sau đó là 'số nguyên tràn'; fi'). 'awk' hoạt động như một nét duyên dáng (' if awk 'BEGIN {return_code = (18446744073692774399> 8589934592)? 0: 1; exit} END {exit return_code} "; sau đó echo' no overflow integer '; fi'). – jaume

59

Plain và đơn giản

#!/bin/bash 

a=2462620 
b=2462620 

if [ "$a" -eq "$b" ];then 
    echo "They're equal"; 
fi 

Bạn có thể kiểm tra this cheatsheet nếu bạn muốn biết thêm comparsions số trong thế giới tuyệt vời của Bash Scripting.

thời gian ngắn, số nguyên chỉ có thể được so sánh với:

-eq # equal 
-ne # not equal 
-lt # less than 
-le # less than or equal 
-gt # greater than 
-ge # greater than or equal 
+0

Tôi chỉ undid thay đổi khác của bạn - dấu ngoặc kép xung quanh '" $ a "' và '" $ b "' là không cần thiết, nhưng họ là thực hành tốt. Niềng răng xoăn không làm bất cứ điều gì hữu ích ở đây. –

0

tôi giải quyết điều này bằng cách sử dụng một chức năng nhỏ để chuyển đổi chuỗi phiên bản với các giá trị số nguyên đơn giản mà có thể được so sánh:

function versionToInt() { 
    local IFS=. 
    parts=($1) 
    let val=1000000*parts[0]+1000*parts[1]+parts[2] 
    echo $val 
} 

Điều này làm cho hai giả định quan trọng:

  1. Đầu vào là "normal SemVer string"
  2. Mỗi phần là giữa 0-999

Ví dụ

versionToInt 12.34.56 # --> 12034056 
versionToInt 1.2.3  # --> 1002003 

Ví dụ thử nghiệm xem npm lệnh đáp ứng yêu cầu tối thiểu ...

NPM_ACTUAL=$(versionToInt $(npm --version)) # Capture npm version 
NPM_REQUIRED=$(versionToInt 4.3.0)   # Desired version 
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then 
    echo "Please update to [email protected]" 
    exit 1 
fi 
+0

với 'sort -V' bạn có thể sắp xếp số phiên bản và sau đó quyết định phải làm gì. Bạn có thể viết một hàm so sánh như sau: hàm version_lt() {test "$ (printf '% s \ n'" $ @ "| sắp xếp -V | đầu -n 1)" == "$ 1"; } và sử dụng nó như sau: nếu version_lt $ v1 $ v2; sau đó ... – koem