Sự khác biệt giữa [[expr1 || expr2]] và [[expr1]] || [[expr2]]


7

Hãy xem xét hai biểu thức điều kiện expr1expr2, ví dụ $i -eq $j$k -eq $l. Chúng ta có thể viết điều này theo bashmột số cách. Đây là hai khả năng

[[ expr1 || expr2 ]]

[[ expr1 ]] || [[ expr2 ]]

Tôi khá chắc chắn rằng tôi đã thấy các khuyến nghị ở đây rằng thứ hai nên được ưu tiên, nhưng tôi không thể tìm thấy bằng chứng để hỗ trợ điều này.

Đây là một kịch bản mẫu dường như chứng minh không có sự khác biệt:

for i in 0 1
do
  for j in 0 1
  do
    for k in 0 1
    do
      for l in 0 1
      do
        if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
        if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
      done
    done
  done
done

và đầu ra cho thấy cả hai cấu trúc điều kiện tạo ra cùng một kết quả:

1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes

Có bất kỳ lợi ích nào khi sử dụng cấu trúc này so với cấu trúc kia không?

Đối với điểm thưởng, cùng một câu hỏi nhưng khái quát cho nhiều điều kiện bằng cách sử dụng ||&&. Ví dụ , [[ expr1 && expr2 || expr3 ]].


Đối với lý do tại sao các khuyến nghị của loại này thường được đưa ra wrt. [hoặc test( không [[ ), tìm kiếm các OBthẻ (được liên kết với định nghĩa "lỗi thời") trong thông số POSIX chotest . Trong testđó, có một số trường hợp bệnh lý trong đó không thể biết được (hoặc )có nghĩa là cú pháp có ý nghĩa đối với lệnh kiểm tra hoặc chuỗi cần kiểm tra hay không, vì vậy những người bỏ qua các dấu hiệu lỗi thời và sử dụng cú pháp đó thực sự có thể cần cách khác- "x$foo"thực hành lỗi thời ; với [[, đó không phải là một vấn đề.
Charles Duffy

Câu trả lời:


7

Tôi nghĩ rằng đề xuất mà bạn đã thấy là dành cho POSIX sh và / hoặc testlệnh nhân đôi [lệnh, thay vì [[cấu trúc xuất hiện trong ksh (cảm ơn Stéphane Chazelas cho mẹo) và cũng được sử dụng ví dụ như trong bash, zsh và một số khác vỏ sò.

Trong hầu hết các ngôn ngữ, như C, khi một mệnh đề đã được biết là đúng hoặc sai, không cần phải đánh giá các phần còn lại tùy thuộc vào thao tác: nếu đúng không theo logic hoặc theo sau nó, nếu sai không theo logic , v.v ... Điều này tất nhiên cho phép ví dụ dừng lại khi một con trỏ là NULL và không cố gắng hủy bỏ nó trong mệnh đề tiếp theo.

Nhưng cấu trúc của sh[ expr1 -o expr2 ] (bao gồm cả việc thực hiện bash) không làm được điều này: nó luôn đánh giá cả hai mặt, khi người ta chỉ muốn expr1 được đánh giá. Điều này có thể đã được thực hiện để tương thích với việc testthực hiện lệnh. Mặt khác, sh ||&&làm theo nguyên tắc thông thường: không được đánh giá nếu nó không thay đổi kết quả.

Vì vậy, sự khác biệt cần lưu ý sẽ là:

: > /tmp/effect #clear effect
if [ 1 -eq 1 -o $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

mang lại:

true
or-was-evaluated

Ở trên, mỗi cái [có thể đã được thay thế bằng /usr/bin/[một bí danh cho testlệnh, được sử dụng trước đó [được tích hợp sẵn vào shell.

Trong khi hai cấu trúc tiếp theo:

: > /tmp/effect #clear effect
if [ 1 -eq 1 ] || [ $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

hoặc là

: > /tmp/effect #clear effect
if [[ 1 -eq 1 || $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]]; then
    echo true;
fi
cat /tmp/effect

Sẽ chỉ mang lại truevà để effecttrống: ||hành xử chính xác, và cũng [[sửa vấn đề này.


CẬP NHẬT:

Như @ StéphaneChazelas đã nhận xét, tôi đã bỏ lỡ một số khác biệt liên quan đến câu hỏi ban đầu. Tôi sẽ chỉ đặt ở đây một điều quan trọng nhất (ít nhất là với tôi): ưu tiên của các nhà khai thác.

Trong khi vỏ sẽ không được coi là ưu tiên:

if true || true && false; then
    echo true
else
    echo false
fi

sản lượng (vì không có quyền ưu tiên và do đó trước tiên true || trueđược đánh giá và sau đó && false):

false

bên [[ ]]các &&nhà điều hành có ưu tiên hơn ||:

if [[ 1 -eq 1 || 1 -eq 1 && 1 -eq 0 ]]; then
    echo true
else
    echo false
fi

sản lượng (vì 1 -eq 1 && 1 -eq 0được nhóm lại và do đó là thành viên thứ 2 của ||):

true

ít nhất là cho ksh , bash , zsh .

Vì vậy, [[ ]]đã cải thiện hành vi trên cả hai [ ]và toán tử logic vỏ trực tiếp.


1
[ x -o y ]không phải đánh giá cả hai bên. Trong khi GNU testhoặc [được xây dựng trong các bashviệc phải làm, bạn sẽ tìm thấy với strace zsh -c '[ x -o -f /x ]'[không cố gắng stat () /x. Tương tự với mksh. Nhưng đó là sự thật -o-abị phá vỡ nghiêm trọng, không thể được sử dụng một cách đáng tin cậy và không được POSIX phản đối.
Stéphane Chazelas

1
Một sự khác biệt là bên trong [[...]], &&có quyền ưu tiên cao hơn ||, trong khi bên ngoài họ có quyền ưu tiên như nhau. So sánh ksh -c '[[ x || x && "" ]]'vớish -c 'true || true && false'
Stéphane Chazelas

1
Lưu ý rằng tiêu chuẩn [/ testtiện ích không có ==toán tử. Toán tử đẳng thức là =(lưu ý rằng bên trong ksh's [[...]], =/ ==không phải là toán tử đẳng thức, mà là các toán tử khớp mẫu).
Stéphane Chazelas

1
Đó là cách khác. &&được ưu tiên hơn ||bên trong [[...]](hoặc ((...))) nhưng không phải là toán tử &&||shell. [[ x || y && z ]]is x || (y && z)while x || y && zis (x || y) && z(toán tử được đánh giá từ trái sang phải mà không có quyền ưu tiên). Tương tự như thế nào *có được ưu tiên hơn +( 1 + 2 * 31 + (2 * 3)) nhưng +-có cùng độ ưu tiên.
Stéphane Chazelas

1
Nếu &&có quyền ưu tiên hơn ||, thì nó nên true || (true && false), thay vì(true || true) && false
Prvt_Yadav
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.