Bash: тест (встроенный) с несколькими аргументами: как они анализируются? - PullRequest
2 голосов
/ 03 марта 2020

Я не использовал эту конкретную конструкцию для bash встроенных test / [ команд, но я столкнулся с ней сегодня и запутался. Это выглядит так:

[ -n "${FOO}" -a -r ${FOO}/bar ] && echo OK

Я знаю, что каждый переключатель делает индивидуально, но я не уверен в поведении, когда они сгруппированы таким образом. В частности, часть -a -r [operand]. Вот что должна сказать страница man:

проверяет и [оценивает условные выражения, используя набор правил, основанный на количестве аргументов.

0 аргументов
Выражение ложно.

1 аргумент
Выражение истинно тогда и только тогда, когда аргумент не равен нулю.

2 аргумента
Если первый аргумент равен!, Выражение будет истинным тогда и только тогда, когда второй аргумент будет нулевым. Если первый аргумент является одним из унарных условных операторов, перечисленных выше в разделе УСЛОВНЫЕ ВЫРАЖЕНИЯ, выражение будет истинным, если унарный тест является истинным. Если первый аргумент не является допустимым унарным условным оператором, выражение является ложным.

3 аргумента
В указанном порядке применяются следующие условия. Если второй аргумент является одним из двоичных условных операторов, перечисленных выше в разделе Условные выражения, то результатом выражения является результат двоичного теста, использующего первый и третий аргументы в качестве операндов. Операторы -a и -o считаются бинарными, когда есть три аргумента. Если первый аргумент -!, Значение является отрицанием теста с двумя аргументами, использующего второй и третий аргументы. Если первый аргумент точно (а третий аргумент точно), результатом является проверка одного аргумента второго аргумента. В противном случае выражение является ложным.

4 аргумента
Если первый аргумент равен!, Результатом является отрицание выражения с тремя аргументами, состоящего из оставшихся аргументов. В противном случае выражение анализируется и оценивается в соответствии с приоритетом, используя правила, перечисленные выше.

5 или более аргументов
Выражение анализируется и оценивается в соответствии с приоритетом, используя правила, перечисленные выше. .

Хорошо, отлично. Так как я предоставляю 5 аргументов, выражение будет проанализировано и применены правила. Я предполагаю, что он будет разбит и оценен на 2 части, например:

[ -n "${FOO}" ] && [ -a -r ${FOO}/bar ] && echo OK

Но это не так, так как это дает [: -r: binary operator expected, ему не нравится [ -a -r ... ]. Конечно, это работает:

[ -n "${FOO}" ] && [ -a ${FOO}/bar ] && [ -r ${FOO}/bar ] && echo OK

И так же:

[ -n "${FOO}" -a ${FOO}/bar ] && echo OK

, но это не удается:

[ -n "${FOO}" -r ${FOO}/bar ] && echo OK

добавление к моей путанице - это та часть, которая говорит:

Операторы -a и -o считаются бинарными операторами, когда есть три аргумента

Как именно -a обрабатывается как бинарный оператор? Я считаю, что он проверяет существование файла, но имеет ли он здесь другое поведение? Что это делает со вторым операндом? Как Bash встроенный test анализирует аргументы в исходном примере? Чего мне не хватает?

Ответы [ 3 ]

1 голос
/ 03 марта 2020

В вашей команде

test -n "${FOO}" -r ${FOO}/bar 

у вас есть два условия, но вы не указываете тесту, должны ли оба быть истинными, или достаточно того, что одно истинно, поэтому вы получаете ошибка. Следовательно, вы должны написать одно из

test -n "${FOO}" -a -r ${FOO}/bar 
test -n "${FOO}" -o -r ${FOO}/bar 

В вашей команде

-n "${FOO}" -a ${FOO}/bar 

у вас также есть два условия, но здесь вы четко говорите (используя -a), что оба должны быть правда. Следовательно, это нормально.

1 голос
/ 03 марта 2020

Вам не нужно -a после анализа, так как && занимает его место. Эквивалентное проанализированное выражение:

[ -n "${FOO}" ] && [ -r ${FOO}/bar ] && echo OK
0 голосов
/ 22 марта 2020

Другие ответы на этот вопрос верны, но я добавлю свои собственные, чтобы прямо указать, что именно привело к моей путанице.

Это относится к встроенным Bash, а также (с некоторыми незначительными изменениями в примерах) BSD / bin / test.

Команда test / [ (я буду использовать test для обозначения обоих) реализует грамматику, которая по своей сути неоднозначна: она будет принимать переключатели -a и -o, но их значение зависит от контекста. При применении в качестве связующего между двумя булевыми операндами -a действует как логический И . Но когда используется как унарный оператор, он проверяет наличие имени файла, указанного в следующем аргументе. Точно так же -o рассматривается как логический ИЛИ , а также как проверка того, включена ли опция оболочки.

Это открывает двери для некоторых странно выглядящих выражений. Как обсуждалось в комментариях к ответу @ user1934428, есть это:

# Assuming that if ${FOO}/bar exists, then it is readable: 

[ -n "${FOO}" -a -r "${FOO}"/bar ] 

# is exactly the same as either of the following:

[ -n "${FOO}" -a -a "${FOO}"/bar ]
[ -n "${FOO}" ] && [ -a "${FOO}"/bar ]

Может быть, это кажется тривиальным? Возможно, вы жаждете путаницы? Я даже не собираюсь пытаться расшифровать это:

# Assuming that ${FOO}/bar exists and is readable, This returns 1:

[ -n "${FOO}" -a -a -a -a "${FOO}"/bar ]

# and this returns 0:

[ -n "${FOO}" -a -a -a -a -o -o -o "${FOO}"/bar ]

Мой вывод из этого достаточно прост: придерживайтесь базовых c и однозначных конструкций при использовании test. Не ткните спящего медведя.

...