Liệu cú pháp của vấn đề không bằng nhau?


22

Khi viết kịch bản, tôi thường viết if của mình với cú pháp sau vì tôi dễ hiểu hơn những gì xảy ra tiếp theo là không đúng.

if [ ! "$1" = "$2" ]; then

Những người khác nói rằng cách dưới đây là tốt hơn

if [ "$1" != "$2" ]; then

Vấn đề là khi tôi hỏi tại sao và liệu có sự khác biệt nào không ai có câu trả lời.

Vì vậy, có sự khác biệt giữa hai cú pháp? Là một trong số họ an toàn hơn so với người khác? Hay đó chỉ là vấn đề sở thích / thói quen?


9
Trong trường hợp đầu tiên mà bạn cần phải biết khai thác ưu tiên để phân biệt !(x==y)từ (!x)==y.
jimmij

@jimmij Bạn có nghĩa là khi so sánh một chuỗi, điều này có if [ ! "$string" = "one" ]nghĩa là nếu không phải là giá trị của $stringbằng one. Và điều này có if [ "$string" != "one"]nghĩa là nếu giá trị của $stringkhông bằng one?
Jimmy_A

5
@Jimmy_A Ý tôi là bạn cần biết cách "dịch" này. Tập hợp các toán tử lại với nhau ( !=cú pháp) chỉ rõ ràng hơn.
jimmij

Thưa các cử tri thân thiết, câu trả lời của Stéphane cho thấy rằng có một sự khác biệt lớn về hành vi, câu trả lời đúng không theo bất kỳ cách nào dựa trên quan điểm.
Kevin

Câu trả lời:


35

Bên cạnh các đối số mỹ phẩm / sở thích, một lý do có thể là có nhiều triển khai trong đó [ ! "$a" = "$b" ]thất bại trong các trường hợp góc hơn so với [ "$a" != "$b" ].

Cả hai trường hợp nên an toàn nếu việc triển khai tuân theo thuật toán POSIX , nhưng ngay cả ngày hôm nay (đầu năm 2018 khi viết), vẫn có những triển khai thất bại. Chẳng hạn, với a='(' b=')':

$ (a='(' b=')'; busybox test "$a" != "$b"; echo "$?")
0
$ (a='(' b=')'; busybox test ! "$a" = "$b"; echo "$?")
1

Với dashcác phiên bản trước 0.5.9, như phiên bản 0.5.8 được tìm thấy như shtrên Ubuntu 16.04 chẳng hạn:

$ a='(' b=')' dash -c '[ "$a" != "$b" ]; echo "$?"'
0
$ a='(' b=')' dash -c '[ ! "$a" = "$b" ]; echo "$?"'
1

(đã sửa trong 0,5.9, xem https://www.mail-archive.com/dash@vger.kernel.org/msg00911.html )

Các triển khai đó xử lý [ ! "(" = ")" ]như [ ! "(" "text" ")" ]vậy [ ! "text" ](kiểm tra xem "văn bản" có phải là chuỗi rỗng không) trong khi POSIX bắt buộc nó phải là [ ! "x" = "y" ](kiểm tra "x" và "y" cho sự bằng nhau). Những triển khai đó thất bại bởi vì họ thực hiện kiểm tra sai trong trường hợp đó.

Lưu ý rằng có một hình thức khác:

! [ "$a" = "$b" ]

Cái đó đòi hỏi phải có vỏ POSIX (sẽ không hoạt động với vỏ Bourne cũ).

Lưu ý rằng một số triển khai đã có vấn đề với [ "$a" = "$b" ](và [ "$a" != "$b" ]) và vẫn làm như tích [hợp /bin/shtrên Solaris 10 (vỏ Bourne, vỏ POSIX đang ở trong /usr/xpg4/bin/sh). Đó là lý do tại sao bạn thấy những thứ như:

[ "x$a" != "x$b" ]

Trong các kịch bản cố gắng để được di động đến các hệ thống cũ.


Vì vậy, nói cách khác, cả hai cú pháp đều làm như nhau, không có sự khác biệt nào, nhưng với ! "$a" = "b"bạn cần phải cẩn thận hơn về cách bạn viết nó để chỉ định so sánh. Từ ví dụ của bạn, tôi hiểu rằng lệnh thứ hai trả về not zerocó thể vừa có lợi hoặc vừa gây rắc rối. Sẽ có ích nếu bạn muốn thoát nếu chúng không khớp hoặc gặp rắc rối nếu bạn muốn xem liệu so sánh có bị lỗi hay không
Jimmy_A

2
@Jimmy_A, không, trong những triển khai [ ! "$a" = "$b" ]chỉ thất bại khi $a($b), nó tuyên bố họ là giống hệt nhau khi họ không, (không phải là chuỗi giống như ), nhưng [thực hiện việc kiểm tra sai trong trường hợp đó.
Stéphane Chazelas

Ahhh, tôi nghĩ rằng tôi đã nhận được nó. Nghĩa thứ nhất và thứ ba có nghĩa là kiểm tra xem điều kiện có đúng không, trong khi thứ hai và thứ tư nếu điều kiện sai. Do đó, các mã trạng thái thoát là khác nhau. Về cơ bản, các biến 1 & 4 là khác nhau và 2 & 3 không bằng nhau.
Jimmy_A

3
@Jimmy_A, không. Bạn đã hoàn toàn bỏ lỡ điểm. Nếu các triển khai này tuân theo POSIX, thì tất cả các mã trạng thái sẽ là 0.
Wildcard

Để chắc chắn rằng tôi hiểu, điều này [ ! "$a" = "$b" ]có nghĩa là : đôi khi được xử lý không chính xác trong các triển khai vỏ lỗi (mà tôi nghĩ là ít nhiều khôi phục câu đầu tiên của câu trả lời).
Michael Burr

8

Các x != ycú pháp là tốt hơn vì ! x == ylà dễ bị lỗi - đòi hỏi kiến thức của các nhà khai thác được ưu tiên mà khác với ngôn ngữ sang ngôn ngữ. Cú pháp ! x == ycó thể được hiểu là !(x == y)hay (!x) == y, tùy theo ưu tiên của !vs =.


Ví dụ, trong c++phủ định ! xuất hiện trước toán tử so sánh / quan hệ == , do đó mã sau:

#include<iostream>

using namespace std;

int main()
{
  int x=1, y=2;
  if(     x  != y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !  x  == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !( x  == y ) ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(   (!x) == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
}

trả lại

true
false
true
false

Hành vi tương tự có thể được quan sát bằng nhiều ngôn ngữ khác, bao gồm ví dụ awk- một công cụ được sử dụng thường xuyên trong thế giới Unix.


Mặt khác, việc tập hợp các toán tử lại với nhau thông qua x != ykhông dẫn đến bất kỳ sự nhầm lẫn nào như một mô hình được thiết lập tốt. Hơn nữa, về mặt kỹ thuật !=thường không phải là hai, mà chỉ là một toán tử, do đó, thậm chí nên nhanh hơn một chút để đánh giá so với so sánh riêng biệt và sau đó phủ định. Do đó, mặc dù cả hai cú pháp đều hoạt động trong bash, tôi khuyên bạn nên làm theo x != yvì nó dễ đọc và duy trì mã hơn theo một số logic tiêu chuẩn.


4

Loại điều này rất dựa trên quan điểm, vì "câu trả lời" phụ thuộc rất nhiều vào cách não của một cá nhân xảy ra để có dây. Mặc dù đúng là về mặt ngữ nghĩa, NOT ( A == B )giống hệt nhau (A != B ), người ta có thể rõ ràng hơn với người này và người kia. Nó cũng phụ thuộc vào ngữ cảnh. Ví dụ: nếu tôi có một bộ cờ, ý nghĩa có thể rõ ràng hơn với một cú pháp so với một cú pháp khác:

if NOT ( fileHandleStatus == FS_OPEN )

như trái ngược với

if ( fileHandleStatus != FS_OPEN )

Và thậm chí if ( FS_OPEN != fileHandleStatus )là kết quả của việc dễ dàng vô tình gõ =thay vì ==trong các ngôn ngữ mà trước đây là bài tập và bài kiểm tra công bằng sau này (như C) ...
derobert
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.