Có sự khác biệt lớn nào khi so sánh một biến như một chuỗi hoặc như một int


22

Vì tò mò, khi thực hiện so sánh biến bash (giá trị của nó là một integer) có thể kiểm tra nó với một số giá trị được xác định trước được khai báo là một inthoặc như một string.

Kịch bản mẫu :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Đầu ra :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

$  ./param.sh blah
$

Cả hai hàm hoạt động theo cùng một cách, và vì vậy tôi tự hỏi liệu có cách nào ưa thích khi kiểm tra một biến số nguyên không? Tôi sẽ đi kiểm tra intso với intvì nó nghiêm ngặt hơn nhưng tôi tự hỏi liệu có bất kỳ trở ngại nào khi thực hiện stringkhông?

Trong trường hợp này, f2()cũng nghiêm ngặt hơn về so sánh, tức là việc chuyển một giá trị thập phân sẽ phá vỡ nó, trong khi đó f1()sẽ không có vấn đề gì.


Lưu ý rằng Bash không thực sự có kiểu dữ liệu Integer. Về cơ bản, bạn có thể chỉ gợi ý Bash để coi Chuỗi là số nguyên.
helpermethod

Câu trả lời:


18

Đúng, rất nhiều sự khác biệt. Chẳng hạn, =kiểm tra sự bằng nhau của chuỗi chính xác, nhưng -eqđánh giá cả hai biểu thức một cách hợp lý trước khi kiểm tra sự bằng nhau:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Ngoài ra, chuỗi trống xảy ra bằng số bằng 0:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

Và một lớp khác biệt khác xuất hiện khi bạn đưa các toán tử so sánh vào - ví dụ như xem xét <so -ltvới:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Điều này là do chuỗi "2" được sắp xếp theo thứ tự abc sau chuỗi "10" (vì 1 đến trước 2), nhưng số "2" nhỏ hơn số "10".


2
Đừng quên cũng có (( ... ))cho các hoạt động số. (( " 1 " == 1 )) && echo yes || echo nokết quả trongyes
Patrick

7

So sánh số nguyên và chuỗi trở nên quan trọng nhất khi bạn so sánh lớn hơn hoặc nhỏ hơn:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

Cái đầu tiên thất bại vì 9 xuất hiện sau 11 khi được sắp xếp theo từ vựng.

Lưu ý rằng việc sử dụng dấu ngoặc kép không xác định xem bạn đang so sánh chuỗi hay số, toán tử thực hiện. Bạn có thể thêm hoặc xóa các trích dẫn ở trên, nó không làm cho bất kỳ sự khác biệt. Bash bắt các biến không xác định trong dấu ngoặc kép, vì vậy dấu ngoặc kép là không cần thiết. Sử dụng dấu ngoặc kép với dấu ngoặc đơn cho các bài kiểm tra số sẽ không giúp bạn tiết kiệm vì:

[ "" -lt 11 ]

dù sao cũng là một lỗi ("yêu cầu biểu thức số nguyên"). Báo giá là một biện pháp bảo vệ hiệu quả với các so sánh chuỗi trong ngoặc đơn:

[ "" \< 11 ]

Lưu ý trong dấu ngoặc kép , ""sẽ -eq 0nhưng không == 0.


1
Trong bash, không nhất thiết phải trích dẫn các biến trong dấu ngoặc kép: nội trang [[đủ thông minh để nhớ các biến đó ở đâu và nó sẽ không bị đánh lừa bởi các biến trống. Dấu ngoặc đơn ( [) không có tính năng này và yêu cầu dấu ngoặc kép.
glenn jackman

@glennjackman Không nhận thấy điều đó. [[ -lt 11 ]]là một lỗi, nhưng nothing=; [[ $nothing -lt 11 ]]không phải là. Tôi đã làm lại đoạn cuối một chút.
goldilocks

2

Ngoài những gì đã được nói.
So sánh cho đẳng thức nhanh hơn với các con số, mặc dù trong kịch bản shell, hiếm khi bạn cần tính toán nhanh.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s

Xem xét rằng họ làm những việc khác nhau, tôi muốn nói rằng hiệu suất là không liên quan - bạn cần sử dụng một trong những gì bạn muốn.
trời ơi

@godlygeek So sánh bình đẳng của một biến có thể đạt được cả hai cách. "-Eq" nhanh hơn.
Emmanuel

Họ kiểm tra các định nghĩa khác nhau về bình đẳng. Nếu bạn muốn trả lời câu hỏi "Biến này có giữ chuỗi chính xác 123" không, bạn chỉ có thể sử dụng =, vì việc sử dụng -eqcũng phù hợp với "+123". Nếu bạn muốn biết "Có phải biến này, khi được đánh giá là biểu thức số học, so sánh bằng 123", bạn chỉ có thể sử dụng -eq. Lần duy nhất tôi có thể thấy nơi một lập trình viên sẽ không quan tâm đến định nghĩa về đẳng thức được sử dụng là khi anh ta biết rằng nội dung của biến bị ràng buộc theo một mẫu cụ thể trước thời hạn.
trời ơi

@godlygeek thú vị, câu hỏi là về việc so sánh sự bằng nhau của các số như thể chúng là các chuỗi, nó có phù hợp với trường hợp các biến bị ràng buộc trước thời hạn với một mẫu cụ thể không?
Emmanuel

Ví dụ của bạn ( b=234) phù hợp với mẫu đó - bạn biết đó không phải là +234 hoặc "234" hoặc "233 + 1", vì bạn đã tự gán nó, vì vậy bạn biết rằng nó so sánh nó như một chuỗi và như một số có giá trị như nhau. Nhưng tập lệnh của OP, vì nó lấy đầu vào làm đối số dòng lệnh, không có ràng buộc đó - hãy xem xét việc gọi nó là ./param.sh 0+1hoặc./param.sh " 1"
godlygeek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.