Почему между & ldquo; [[& rdquo; требуется пробел и & ldquo; -e xxx & rdquo; в кш?

Например, следующая команда не работает:

if [[-e xyz]]; then echo File exists;fi

ksh выдает следующую ошибку

[[-e: command not found

Это потому, что «[[-» неоднозначно?

9
задан 23.06.2019, 11:13

5 ответов

Самое простое объяснение было бы, потому что в руководстве оно выглядит как [[ expression ]], поэтому должно быть пространство между [[ и expression и закрытием ]]. Но, конечно, мы можем попытаться взглянуть на это более подробно. Оболочки имеют несколько сложную грамматику и в значительной степени полагаются на понятие «разбиение слов». В частности, руководство ksh (1) гласит:

Оболочка начинает анализировать входные данные, разбивая их на слова. Слова, являющиеся последовательностями символов, отделяются символами пробела без кавычек (пробел, табуляция и новая строка) или метасимволами (& lt ;,>, |,;, & amp ;, (и)). Помимо разграничения слов, пробелы и символы табуляции игнорируются, а символы новой строки обычно разделяют команды.

Таким образом, как указано в руководстве, последовательность символов [[-e считается словом оболочки. ksh будет искать такую ​​команду в списке встроенных и специальных операторов (например, for или while), затем искать внешнюю команду - и вуаля - ни одна не найдена, поэтому появляется сообщение об ошибке о команде not найдено.

В этом случае [[ также не являются специальными метасимволами. Если бы они были, часть -e в [[-e считалась бы словом-оболочкой, а не аргументом для самого [[. Однако [[ описывается как составная команда, а в руководстве говорится следующее:

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

Таким образом, чтобы [[ было распознано как составная команда, она имеет быть первым словом команды или списка команд, что, согласно предыдущему определению слова, должно быть отделено пробелами, символами табуляции или переводами строки от других слов / аргументов.


Вы спросили в комментариях : «Почему парсер не может остановиться, как только найдет шаблон» [[», и рассматривает это как начало условная команда? Краткий ответ, вероятно, объясняется тем, что: 1) влияние синтаксиса [, поскольку оно изначально было внешней командой, и для соответствия стандарту POSIX существует как внешняя команда даже сегодня, и 2) потому что анализатор оболочки построен так. Анализатор оболочки может распознавать другие специальные символы без пробелов: echo $((2+2)) и (echo foobar) работают отлично. Может быть, в будущем, когда ksh разработка возобновится или появится форк или клон (например, mksh или pdksh), кто-то внедрит синтаксис без пробелов в [[-e.

См. Также:

15
ответ дан 24.10.2019, 12:57
  • 1
    Я думаю, что руководство по ksh действительно на месте. Это похоже на любой другой язык программирования: в C char является ключевым словом, но вы все же не можете написать charsomeletter = 'A'; и ожидать, что парсер остановится после просмотра char. – Community 23.06.2019, 17:44
  • 2
    Я думаю, что основное различие между вашим примером "char" и символ & quot; [[& quot; вопрос в том, что буквенно-цифровой идентификатор, как правило, нуждается в разделителе пробела для отделения от других; специальные символы как токены, с другой стороны, не нуждаются в разделителях пробелов. – wisbucky 24.06.2019, 04:49
  • 3
    Именно так. Я не уверен, что сравнение с Си полезно. В Си можно сказать i--<=++j, и у компилятора нет проблем с его разбором как i -- <= ++ j. – Greg Hewgill 24.06.2019, 05:05
  • 4
    @ Scott Я думаю, что сравнение C полезно (и что это своего рода вторично, что идентификаторы C допускают только буквенно-цифровые символы и _). Ksh имеет ( в качестве оператора , а (echo hello) анализирует как ( echo hello ). Он имеет if, {, [[ и другие ключевые слова , а также ifx, {x и [[x - каждый токен, как charsomeletter один токен C. Напротив, без кавычек (x в ksh есть два токена - например, как i-- - два токена в C. То, что концептуально даже означает быть оператором, различается между оболочками в стиле Борна ksh) и C. Но Питер А. Шнайдер указал на реальное лексическое сходство. – Greg Hewgill 24.06.2019, 08:13

Поскольку [[-e может участвовать в расширении оболочки (его можно использовать как

echo [[-e]*

для того, чтобы вывести список всех файлов, начинающихся с буквы между [ и e включительно), он будет был бы полный беспорядок, если бы [ и ] были специальными символами, не участвующими в обычном разделении пробелов.

0
ответ дан 24.10.2019, 12:57

Пространство является разделителем и является обязательным. Как вы можете видеть из shellcheck :

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(и ksh, и bash поддерживают [[ и не работают без пробела. Shellcheck выдает именно такой вывод с аналогичным ksh и bash-скрипты, содержащие эту строку с ошибками.)

Зачем нужны разделители, связано с токенами и лексиконами .

2
ответ дан 24.10.2019, 12:57
  • 1
    Обратите внимание, что ОП задал вопрос о ksh оболочке в этом вопросе. Баш заимствует оператор [[ из ksh, и в этом вопросе их поведение идентично, но я буду осторожен с предположениями, поскольку существуют некоторые существенные различия между поведением ksh и bash и внутренними объектами. Это потому, что shellcheck работает на скрипте bash, он не обязательно может быть подходящим инструментом для скриптов ksh. Просто что-то иметь в виду при приближении к различным снарядам – halfer 24.06.2019, 05:10
  • 2
    @SergiyKolodyazhnyy Я был оппортунистом, используя shellcheck, чтобы выделить встроенные правила [[. Я ни в коем случае не делал вывод, что ksh была оболочкой, в которой shellcheck мог обнаруживать ошибки. Я надеюсь, что другие ценят ksh и bash - два разных интерпретатора. Спасибо за упоминание этого. Также стоит отметить, что [[ - встроенная команда bash, а [ - внешняя команда. – stian 24.06.2019, 05:52
  • 3
    На самом деле [ также является встроенным в bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Но вы не далеко - [ или test должна быть внешней командой. Во времена оригинальной оболочки Bourne [ фактически была внешней командой и до сих пор существует как /usr/bin/[, потому что POSIX требует, чтобы она была внешней командой pubs.opengroup.org/onlinepubs/009695399/utilities/test .html POSIX требуется очень мало для того, чтобы быть встроенным pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Встроенный [ предназначен для повышения эффективности. – Ørjan Johansen 24.06.2019, 06:08
  • 4
    Шеллчек знает ksh; используйте hashbang или -s ksh. Кстати, у ksh и bash есть [[ «встроенный» но это ключевое слово , в отличие от [, который является встроенной оболочкой . (ksh: whence -v [ [[; bash: type [ [[) Встроенные и внешние команды синтаксически похожи. Одним из отличий [[ от [ является то, что [[ подавляет некоторые расширения в своих аргументах, которые он не может встроить - так же, как { не может группировать, если он встроен. Возможно, поэтому {x (или [[x), будучи одним токеном, кажется странным. Я думаю, что правильно сказать, что встроенные и ключевые слова лексически , но не похожи синтаксически . @SergiyKolodyazhnyy – Daniel Huckstep 24.06.2019, 08:36
  • 5
    @EliahKagan На самом деле я разместил вопрос на U & amp; L, чтобы получить точный ответ о том, что POSIX думает об этой ситуации unix.stackexchange.com/questions/526574/… Язык оболочки достаточно сложен, поэтому надеюсь, кто-то может объяснить это в более формальных терминах – Daniel Huckstep 24.06.2019, 12:11

[[ может быть внешней командой! То есть это может быть программа, а не какой-то синтаксис, поддерживаемый вашей оболочкой напрямую. Поддержка синтаксиса без пробелов была бы возможна для ksh, но она не работала бы в системах с внешним [[, поэтому по соображениям совместимости лучше сохранить требуемое пространство.

BusyBox предоставляет [[ как внешнюю команду , например.

1
ответ дан 24.10.2019, 12:57

Важно отметить, что [ является командой, а ключевое слово shell [[ в bash и ksh основано на [.

[[ используется аналогично [. Иногда вы даже можете заменить [ ] на [[ ]] без изменений в поведении. В [, как и в любой команде, между командой и ее аргументами обязательно должен быть пробел. То же самое относится к [[.

Раньше у нас было только /usr/bin/[. Сейчас большинство оболочек имеют встроенную [ для повышения эффективности, но синтаксис тот же. В оболочках, которые предоставляют [[, он функционирует как более универсальная альтернатива [.

Вот описание для [ в bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Итак, [ эквивалентно test (кроме ожидания аргумента ] в самом конце). help test даст еще больше подробностей об этом. Вы можете сравнить это с help [[.

Существует также страница руководства для внешней команды [ ( man \[ ).

В случае

if [[-e
  • Ни [, ни [[ не является командой. Полное слово [[-e есть.
  • if [[-e делает его тестом на истину / ложь. Так существует ли команда [[-e?

Это потому, что «[[-» неоднозначно?

Да. Или нет [[-e - это то, что есть: ничего, что оболочка не понимает, поэтому она предполагает, что это ее собственная команда. ;-)

2
ответ дан 24.10.2019, 12:57
  • 1
    & quot;% help [[& quot; говорит, что & quot; [[& quot; это условная команда. Почему анализатор не может остановиться, как только обнаружит & quot; [[& quot; шаблон, и рассматривает это как начало условной команды? – Ben 23.06.2019, 04:21
  • 2
    @Myloti [[-e - это потенциально допустимое (хотя и странное) имя команды. – Greg Hewgill 23.06.2019, 09:01

Теги

Похожие вопросы