Ưu tiên của ống (|) và logic và (&&) trong bash


17

Kịch bản cổ điển với Toán tử ưu tiên, bạn có một dòng như:

(cd ~/screenshots/ && ls screenshot* | head -n 5)

Và bạn không biết nếu nó được phân tích cú pháp ((A && B) | C)hay (A && B | C)...

Các tài liệu gần như chính thức được tìm thấy ở đây không liệt kê các đường ống trong danh sách vì vậy tôi không thể chỉ cần kiểm tra trong bảng.

Hơn nữa, trong bash, (không chỉ để thay đổi thứ tự các hoạt động mà còn tạo ra một mạng con , vì vậy tôi không chắc chắn 100% dòng này tương đương với dòng trước:

((cd ~/screenshots/ && ls screenshot*) | head -n 5)

Tổng quát hơn, làm thế nào để biết AST của một dòng bash? Trong python tôi có một chức năng cung cấp cho tôi cây để tôi có thể dễ dàng kiểm tra lại thứ tự hoạt động.


1
Có thể giúp biết rằng đó |chỉ là một trình kết nối phù hợp với thiết bị xuất chuẩn LHS cho stdin RHS. Vì vậy, nếu cdlệnh trong ví dụ của bạn thất bại, nó sẽ không gửi đầu ra head, nhưng headthực tế vẫn sẽ execute, phân tích không có gì và không trả lại đầu ra.
DopeGhoti


1
bashđang sử dụng một trình phân tích cú pháp yacc chứ không phải một số thứ đặc biệt; nếu bạn chạy yacc -vnó thông qua nó sẽ cung cấp cho bạn y.outputmột ngữ pháp tốt cho thấy điều đó &&||kết hợp các danh sách, và danh sách cuối cùng được tạo thành từ các đường ống dẫn (chứ không phải ngược lại); tl; dr; A && B | Clà giống như A && { B | C; }, như mong đợi. Đừng giả sử bất kỳ thứ tự thực hiện giữa BC; các lệnh trong một đường ống được chạy song song .
mosvy

1
Lưu ý rằng "tài liệu gần như chính thức" mà bạn chỉ ra là hoàn toàn không liên quan, vì đó là về các toán tử được sử dụng trong [...]các bài kiểm tra và $((...))đánh giá số học; đặc biệt, ||&&như được sử dụng với danh sách lệnh trong ngôn ngữ shell có cùng mức độ ưu tiên , không giống như các toán tử tương ứng trong Choặc trong đánh giá số học (nơi &&liên kết chặt chẽ hơn ||).
mosvy

@mosvy, tài liệu đó thậm chí không nói bối cảnh mà bảng áp dụng trong đó, vì vậy nó có vẻ ít hữu ích hơn theo nghĩa đó ...
ilkkachu

Câu trả lời:


20
cd ~/screenshots/ && ls screenshot* | head -n 5

Điều này tương đương với

cd ~/screenshots && { ls screenshot* | head -n 5 ; }

(các lệnh niềng răng nhóm với nhau mà không có một khung con ). |Do đó, mức độ ưu tiên cao hơn (liên kết chặt chẽ hơn) &&||. Đó là,

A && B | C

A || B | C

luôn luôn có nghĩa là đầu ra chỉ B sẽ được trao cho C . Bạn có thể sử dụng (...)hoặc { ... ; }để nối các lệnh với nhau dưới dạng một thực thể duy nhất để định hướng nếu cần:

{ A && B ; } | C
A && { B | C ; } # This is the default, but you might sometimes want to be explicit

Bạn có thể kiểm tra điều này bằng cách sử dụng một số lệnh khác nhau. Nếu bạn chạy

echo hello && echo world | tr a-z A-Z

sau đó bạn sẽ nhận được

hello
WORLD

trở lại: tr a-z A-Zcác trường hợp đầu vào của nó , và bạn có thể thấy rằng chỉ echo worldđược đưa vào trong đó, trong khi tự mình echo hellođi qua.


Điều này được định nghĩa trong ngữ pháp shell , mặc dù không rõ ràng lắm: and_orsản xuất (for &&/ ||) được xác định là có aa pipelinetrong cơ thể của nó, trong khi pipelinechỉ chứa command, không chứa and_or- chỉ có complete_commandsản phẩm có thể đạt tới and_orvà nó chỉ tồn tại ở cấp cao nhất và bên trong cơ thể của các cấu trúc cấu trúc như các hàm và vòng lặp.

Bạn có thể tự áp dụng ngữ pháp đó để lấy cây phân tích cú pháp cho một lệnh, nhưng Bash không cung cấp bất cứ thứ gì. Tôi không biết bất kỳ shell nào vượt quá những gì được sử dụng để phân tích cú pháp của riêng họ.

Ngữ pháp shell có rất nhiều trường hợp đặc biệt chỉ được định nghĩa bán chính thức và nó có thể là một nhiệm vụ hoàn toàn đúng đắn. Ngay cả chính Bash đôi khi cũng hiểu sai , vì vậy tính thực tiễn và lý tưởng có thể khác nhau.

Có những trình phân tích cú pháp bên ngoài cố gắng khớp cú pháp và tạo ra một cây, và trong số đó tôi sẽ giới thiệu rộng rãi Morbig , đây là nỗ lực đáng tin cậy nhất.


7

TL; DR : danh sách phân tách như ;. &, &&||quyết định thứ tự phân tích cú pháp.

Các nhãn hiệu bash cho chúng ta biết:

Danh sách AND và OR là các chuỗi của một hoặc nhiều đường ống được phân tách bằng && và || toán tử điều khiển, tương ứng.

Hoặc làm thế nào wiki của Bash Hacker ngắn gọn đặt nó

<PIPELINE1> && <PIPELINE2>

Do đó, trong cd ~/screenshots/ && ls screenshot* | head -n 5 đó có một đường ống dẫn - ls screenshot* | head -n 5và một lệnh đơn giản cd ~/screenshots/. Lưu ý rằng theo hướng dẫn

Mỗi lệnh trong một đường ống được thực thi như một quy trình riêng biệt (nghĩa là trong một lớp con).

Mặt khác, (cd ~/screenshots/ && ls screenshot*) | head -n 5khác biệt - bạn có một đường ống dẫn: bên trái có subshell và bên phải bạn có head -n 5. Trong trường hợp này, sử dụng ký hiệu của OP sẽ là(A && B) | C


Hãy lấy một ví dụ khác:

$ echo foo | false &&  echo 123 | tr 2 5
$

Ở đây chúng tôi có một danh sách <pipeline1> && <pipeline2>. Vì chúng ta biết rằng trạng thái thoát của đường ống giống như lệnh cuối cùng và falsetrả về trạng thái phủ định aka thất bại, &&sẽ không thực hiện phía bên phải.

$ echo foo | true &&  echo 123 | tr 2 5
153

Ở đây, đường ống bên trái có trạng thái thoát thành công, vì vậy đường ống bên phải được thực thi và chúng ta thấy đầu ra của nó.


Lưu ý rằng ngữ pháp shell không ngụ ý thứ tự thực hiện thực tế. Để trích dẫn một trong những câu trả lời của Gilles :

Các lệnh đường ống chạy đồng thời. Khi bạn chạy ps | grep, đó là sự may mắn của trận hòa (hoặc vấn đề chi tiết về hoạt động của vỏ kết hợp với bộ điều chỉnh lịch trình sâu trong ruột của hạt nhân) về việc ps hay grep bắt đầu trước, và trong mọi trường hợp chúng vẫn tiếp tục để thực hiện đồng thời.

Và từ hướng dẫn bash:

Danh sách AND và OR được thực thi với tính kết hợp trái.

Dựa vào đó cd ~/screenshots/ && ls screenshot* | head -n 5, cd ~/screenshots/sẽ được thực thi trước, ls screenshot* | head -n 5nếu lệnh trước thành công, nhưng head -n 5có thể là quá trình đầu tiên được sinh ra thay lsvì chúng nằm trong một đường ống.


1
Tôi không thấy câu hỏi hỏi bất cứ điều gì về thứ tự được sinh ra.
Michael Homer

"không có quyền ưu tiên mà người ta sinh ra trước, cd ~/screenshots/ && ls screenshot*hoặc head -n 5" Vâng, vâng, đó là trường hợp ((cd ~/screenshots/ && ls screenshot*) | head -n 5). Nhưng nó không nói gì về trường hợp mơ hồ cd ~/screenshots/ && ls screenshot* | head -n 5dường như là điểm của câu hỏi (có hoặc không có dấu ngoặc đơn xung quanh).
ilkkachu

@ilkkachu Đúng, nhưng những câu sau chạm vào đó. cd ~/screenshots/ && ls screenshot* sẽ phải được xử lý trước vì &&danh sách cao hơn theo thứ tự ưu tiên (ít nhất là dựa trên câu trả lời của l0b0). Bạn nghĩ tôi nên cải thiện câu trả lời ở đâu?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy, tốt, cho rằng câu hỏi có cả hai (cd && ls | head)((cd && ls) | head), có thể tốt để nói rõ về ý của bạn.
ilkkachu

@ilkkachu OK, tôi sẽ xóa cái này ngay bây giờ và chỉnh sửa nó trong lúc này
Sergiy Kolodyazhnyy

3

Đây là nơi nó được chỉ định trong bash(1):

SHELL GRAMMAR
[...]
   Pipelines
       A  pipeline  is  a sequence of one or more commands separated by one of
       the control operators | or |&.
[...]
   Lists
       A list is a sequence of one or more pipelines separated by one  of  the
       operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or
       <newline>.

Vì vậy, &&tách đường ống.


2

Bạn chỉ có thể thử echo hello && echo world | less. Bạn sẽ thấy rằng |có quyền ưu tiên cao hơn (Một đường dẫn các lệnh là một lệnh). Có ví dụ thứ 2 của bạn KHÔNG giống nhau. Tuy nhiên vì cdkhông có đầu ra, bạn sẽ thấy không có sự khác biệt, cách ether.

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.