Перевага труби (|) та логічного та (&&) в баші


17

Класичний сценарій з операторською перевагою, у вас є такий рядок:

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

І ви не знаєте, чи розібраний він ((A && B) | C) чи (A && B | C)...

Майже офіційна документація FOUND тут не перераховує трубку в списку , так що я не можу просто перевірити в таблиці.

Крім того, в bash, (це не тільки для зміни порядку операцій, але створює підзарядку , тому я не на 100% впевнений, що цей рядок є еквівалентом попереднього рядка:

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

Більш загально, як знати AST лінії баш? У python у мене є функція, яка дає мені дерево, щоб я міг легко двічі перевірити порядок роботи.


1
Можливо, це допоможе дізнатися, що |це лише роз'єм, який відповідає LHS-виходу в STS RHS. Отже, якщо cdкоманда у вашому прикладі не вдасться, вона не надіслала б ніякого виводу head, але headнасправді все-таки executeпроаналізувала б нічого і не поверне жодного результату.
DopeGhoti


1
bashвикористовує аналізатор yacc не якусь спеціальну річ; якщо запустити це через yacc -vнього, ви отримаєте y.outputгарну граматику, яка показує, що &&і ||комбінувати списки, а списки зрештою складаються з конвеєрів (а не зворотного); tl; dr; A && B | Cтаке саме A && { B | C; }, як і очікувалося. Не приймайте жодного порядку виконання між Bта C; команди в трубопроводі виконуються паралельно .
mosvy

1
Зауважте, що «майже офіційна документація», яку ви вказуєте, абсолютно не має значення, оскільки йдеться про операторів, які використовуються всередині [...]тестів та $((...))арифметичних оцінок; зокрема, ||і &&як це використовується у списку команд на мові оболонки, має однаковий пріоритет , на відміну від відповідних операторів в Cабо в арифметичній оцінці (де &&пов'язується більш щільно, ніж ||).
мосви

@mosvy, цей документ навіть не говорить про те, в якому контексті застосовується таблиця, тому він здається менш корисним і в цьому сенсі ...
ilkkachu

Відповіді:


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

Це еквівалентно

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

(команди брекетів команд разом без підзаголовок ). Таким чином, пріоритет |вище (зв'язується жорсткіше) ніж &&та ||. Тобто,

A && B | C

і

A || B | C

завжди це означає вихід тільки Б має бути дано C . Ви можете використовувати (...)або { ... ; }приєднувати команди разом як єдине ціле для розбірливості, якщо це необхідно:

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

Ви можете перевірити це за допомогою декількох різних команд. Якщо ти біжиш

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

тоді ви отримаєте

hello
WORLD

назад: tr a-z A-Z великі регістри його введення , і ви можете бачити, що тільки echo worldвкладені в нього, в той час як echo helloпроходив самостійно.


Це буде визначено в граматиці оболонки , хоча і не надто чітко: на and_orвиробництво (для &&/ ||) визначається , щоб мати аа pipelineв своєму тілі, в той час як pipelineмістить тількиcommand , який НЕ містить and_or- тількиcomplete_command виробництво може досягти and_or, і він існує лише на верхній рівень і всередині тіл структурних конструкцій, як функції і петлі.

Ви можете застосувати цю граматику вручну, щоб отримати дерево розбору для команди, але Bash нічого не забезпечує. Я не знаю жодної оболонки, яка перевищує те, що використовується для їх власного розбору.

Граматика оболонки має чимало особливих випадків, визначених лише напівформально, і це може бути досить місією, щоб виправитись. Навіть сам Баш іноді помилявся , тому практичність та ідеал можуть бути різними.

Є зовнішні аналізатори, які намагаються відповідати синтаксису і створювати дерево, і серед них я широко рекомендую Морбіга , який намагається бути найбільш надійним.


7

TL; DR : роздільники списку, такі як ;. &,&& і ||визначити порядок розбору.

Посібник з bash говорить нам:

Списки AND і OR - це послідовності одного або декількох трубопроводів, розділених && та || оператори управління відповідно.

Або як лаконічно сказала вікі Баша Хакера

<PIPELINE1> && <PIPELINE2>

Таким чином, cd ~/screenshots/ && ls screenshot* | head -n 5 існує один конвеєр - ls screenshot* | head -n 5і одна проста команда cd ~/screenshots/. Зауважте, що згідно з посібником

Кожна команда в конвеєрі виконується як окремий процес (тобто в підпакеті).

З іншого боку, (cd ~/screenshots/ && ls screenshot*) | head -n 5інакше - у вас є один трубопровід: зліва - нижня оболонка, а справа - у вас head -n 5. У цьому випадку, використовуючи позначення ОП, це було б(A && B) | C


Візьмемо ще один приклад:

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

Тут ми маємо один список <pipeline1> && <pipeline2>. Оскільки ми знаємо, що статус виходу конвеєра такий же, як і в останньої команди, і falseповертає негативний статус aka fail, &&не буде виконуватися права сторона.

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

Тут лівий трубопровід має статус успішного виходу, тому правий трубопровід виконується і ми бачимо його вихід.


Зауважте, що граматика оболонки не означає фактичного порядку виконання. Цитую одну з відповідей Жилла :

Командні труби виконують одночасно. При запуску ps | grep…, це щастя жеребкування (або питання деталей роботи оболонки в поєднанні з плановою детальною настройкою глибоко в надрах ядра) щодо того, чи починається ps чи grep спочатку, і в будь-якому випадку вони продовжуються виконувати одночасно.

І з керівництва bash:

Списки AND і OR виконуються з лівою асоціативністю.

Виходячи з цього, у cd ~/screenshots/ && ls screenshot* | head -n 5програмі cd ~/screenshots/буде виконуватися спочатку, ls screenshot* | head -n 5якщо попередня команда буде успішною, але, head -n 5можливо, перший процес породжується, а не lsтому, що вони знаходяться в конвеєрі.


1
Я не бачу, де запитання нічого не задає про те, в якому порядку породжуються речі.
Майкл Гомер

"немає жодного пріоритету того, хто породжує першим, cd ~/screenshots/ && ls screenshot*або head -n 5" Ну так, так це ((cd ~/screenshots/ && ls screenshot*) | head -n 5). Але це нічого не говорить про неоднозначний випадок, cd ~/screenshots/ && ls screenshot* | head -n 5який, здається, є пунктом питання (з навколишніми дужками або без них).
ilkkachu

@ilkkachu Право, але наступні речення стосуються цього. cd ~/screenshots/ && ls screenshot* спочатку слід обробити, оскільки &&списки вищі за порядком пріоритетності (виходячи з відповіді l0b0, принаймні). Як ви думаєте, де мені слід покращити відповідь?
Сергій Колодяжний

@SergiyKolodyazhnyy, ну, враховуючи, що питання має і те, (cd && ls | head)і ((cd && ls) | head), можливо, було б чітко сказати про те, який саме ви маєте на увазі.
ilkkachu

@ilkkachu Добре, я зараз видалю це, а тим часом відредагую його
Сергій Колодяжний

3

Ось де це вказано в 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>.

Отже, &&розділяє трубопроводи.


2

Ви можете просто спробувати echo hello && echo world | less. Ви побачите, що |має більшу перевагу (Трубопровід команд - це команда). Там для вашого 2-го прикладу НЕ те саме. Однак, оскільки cdвиходу немає, ефірним шляхом ви не побачите різниці.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.