Классический сценарий с приоритетом оператора, у вас есть такая строка:
(cd ~/screenshots/ && ls screenshot* | head -n 5)
И вы не знаете, если это проанализировано ((A && B) | C)
или (A && B | C)
...
Почти официальная документация нашла здесь не перечисляет трубку в списке , так что я не могу просто проверить в таблице.
Кроме того, в bash он (
предназначен не только для изменения порядка операций, но и создает подоболочку , поэтому я не уверен на 100%, что эти строки эквивалентны предыдущей строке:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
В общем, как узнать AST линии bash? В Python у меня есть функция, которая дает мне дерево, чтобы я мог легко проверить порядок операций.
|
это просто разъем, который подходит для стандартного вывода LHS и стандартного подключения RHS. Таким образом, еслиcd
команда в вашем примере завершится неудачно, она не будет отправлять выходные данныеhead
, ноhead
на самом деле все равно будетexecute
анализировать ничего и не будет возвращать никаких результатов.bash
использует парсер yacc не какую-то специальную вещь; если вы пройдете черезyacc -v
это, вы получитеy.output
красивую грамматику, которая показывает это&&
и||
объединяет списки, а списки в конечном итоге состоят из конвейеров (а не наоборот); ТЛ; др;A && B | C
так жеA && { B | C; }
, как и ожидалось. Не принимайте никакой порядок исполнения междуB
иC
; Команды в конвейере выполняются параллельно .[...]
тестов и$((...))
арифметических оценок; в частности,||
и&&
при использовании со списком команд в языке оболочки имеют тот же приоритет , в отличие от соответствующих операторов вC
или в арифметической оценке (где&&
связывается более тесно, чем||
).Ответы:
Это эквивалентно
( брекеты группируют команды без подоболочки ). Таким образом, приоритет
|
выше (связывается теснее), чем&&
и||
. То есть,и
всегда означает , что выход только Б должно быть дано C . Вы можете использовать
(...)
или{ ... ; }
объединять команды как единый объект для устранения неоднозначности, если необходимо:Вы можете проверить это, используя несколько разных команд. Если вы бежите
тогда вы получите
назад:
tr a-z A-Z
заглавные буквы его ввода , и вы можете видеть, что толькоecho world
был передан в него, покаecho hello
прошел сам по себе.Это будет определено в грамматике оболочки , хотя и не очень четко: на
and_or
производство (для&&
/||
) определяется , чтобы иметь ааpipeline
в своем теле, в то время какpipeline
содержит толькоcommand
, который не содержитand_or
- толькоcomplete_command
производство может достигатьand_or
, и он существует только на верхний уровень и внутри тел структурных конструкций, таких как функции и петли.Вы можете вручную применить эту грамматику, чтобы получить дерево разбора для команды, но Bash сам ничего не предоставляет. Я не знаю ни одной оболочки, которая выходит за рамки того, что используется для их собственного анализа.
Грамматика оболочки имеет множество особых случаев, определенных лишь полуформально, и это может быть довольно сложной задачей, чтобы получить право. Даже сам Баш иногда ошибался , поэтому практичность и идеал могут быть разными.
Существуют внешние парсеры, которые пытаются сопоставить синтаксис и создать дерево, и я буду широко рекомендовать Morbig , который пытается быть наиболее надежным.
источник
TL; DR : разделители списка, такие как
;
.&
,&&
И||
определять порядок разбора.Руководство по bash говорит нам:
Или как вкратце выразить вики Bash Hacker
Таким образом,
cd ~/screenshots/ && ls screenshot* | head -n 5
есть один конвейер -ls screenshot* | head -n 5
и одна простая командаcd ~/screenshots/
. Обратите внимание, что в соответствии с руководствомС другой стороны,
(cd ~/screenshots/ && ls screenshot*) | head -n 5
все по-другому - у вас есть один конвейер: слева есть подоболочка, а справа - выhead -n 5
. В этом случае, используя запись OP, это будет(A && B) | C
Давайте возьмем другой пример:
Здесь у нас есть один список
<pipeline1> && <pipeline2>
. Поскольку мы знаем, что состояние выхода из конвейера такое же, как и в последней команде, иfalse
возвращает отрицательное состояние, то есть сбой,&&
правая сторона не будет выполняться.Здесь левый конвейер имеет статус успешного завершения, поэтому правый конвейер выполняется, и мы видим его вывод.
Обратите внимание, что грамматика оболочки не подразумевает фактического порядка выполнения. Процитирую один из ответов Жиля :
И из руководства Bash:
Основываясь на том, что
cd ~/screenshots/ && ls screenshot* | head -n 5
командаcd ~/screenshots/
будет выполнена первой,ls screenshot* | head -n 5
если предыдущая команда выполнена успешно, ноhead -n 5
может быть первым порожденным процессом, а неls
потому что они находятся в конвейере.источник
cd ~/screenshots/ && ls screenshot*
илиhead -n 5
" Ну, да, это так((cd ~/screenshots/ && ls screenshot*) | head -n 5)
. Но это ничего не говорит о неоднозначном случае,cd ~/screenshots/ && ls screenshot* | head -n 5
который, кажется, является предметом вопроса (с или без круглых скобок).cd ~/screenshots/ && ls screenshot*
сначала нужно будет обработать, потому что&&
списки выше по порядку старшинства (по крайней мере, на основании ответа l0b0). Как вы думаете, где я должен улучшить ответ?(cd && ls | head)
и((cd && ls) | head)
, возможно, было бы хорошо четко указать, какой из них вы имеете в виду.Вот где это указано в
bash(1)
:Итак,
&&
разделяет трубопроводы.источник
Вы можете просто попробовать
echo hello && echo world | less
. Вы увидите, что|
имеет более высокий приоритет (конвейер команд является командой). Там для вашего 2-го примера это не то же самое. Однако, посколькуcd
не имеет выхода, вы не увидите никакой разницы, эфирный путь.источник