Например, следующая команда не работает:
if [[-e xyz]]; then echo File exists;fi
ksh выдает следующую ошибку
[[-e: command not found
Это потому, что «[[-» неоднозначно?
Самое простое объяснение было бы, потому что в руководстве оно выглядит как [[ 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
.
[[
называется «зарезервированным словом» (см. раздел 2.9 языка командных команд оболочки ). По определению POSIX зарезервированное слово должно быть разделено пробелами. В отличие от этого (
является оператором, и стандартные положения в разделе 2.9 «представления включают в себя расстояние между токенами в некоторых местах, где <blank>
s не потребуется (когда один из токенов является оператором)». См. Обсуждение по теме: Грамматика оболочки POSIX: почему группе скобок нужен первый пробел, а субсколу нет? Поскольку [[-e
может участвовать в расширении оболочки (его можно использовать как
echo [[-e]*
для того, чтобы вывести список всех файлов, начинающихся с буквы между [
и e
включительно), он будет был бы полный беспорядок, если бы [
и ]
были специальными символами, не участвующими в обычном разделении пробелов.
Пространство является разделителем и является обязательным. Как вы можете видеть из 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-скрипты, содержащие эту строку с ошибками.)
Зачем нужны разделители, связано с токенами и лексиконами .
ksh
оболочке в этом вопросе. Баш заимствует оператор [[
из ksh
, и в этом вопросе их поведение идентично, но я буду осторожен с предположениями, поскольку существуют некоторые существенные различия между поведением ksh
и bash
и внутренними объектами. Это потому, что shellcheck работает на скрипте bash, он не обязательно может быть подходящим инструментом для скриптов ksh. Просто что-то иметь в виду при приближении к различным снарядам
– halfer
24.06.2019, 05:10
[[
. Я ни в коем случае не делал вывод, что ksh
была оболочкой, в которой shellcheck
мог обнаруживать ошибки. Я надеюсь, что другие ценят ksh
и bash
- два разных интерпретатора. Спасибо за упоминание этого. Также стоит отметить, что [[
- встроенная команда bash, а [
- внешняя команда.
– stian
24.06.2019, 05:52
[
также является встроенным в 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
ksh
; используйте hashbang или -s ksh
. Кстати, у ksh и bash есть [[
«встроенный» но это ключевое слово i>, в отличие от [
, который является встроенной оболочкой i>. (ksh: whence -v [ [[
; bash: type [ [[
) Встроенные и внешние команды синтаксически похожи. Одним из отличий [[
от [
является то, что [[
подавляет некоторые расширения в своих аргументах, которые он не может встроить - так же, как {
не может группировать, если он встроен. Возможно, поэтому {x
(или [[x
), будучи одним токеном, кажется странным. Я думаю, что правильно сказать, что встроенные и ключевые слова лексически i>, но не похожи синтаксически i>. @SergiyKolodyazhnyy
– Daniel Huckstep
24.06.2019, 08:36
[[
может быть внешней командой! То есть это может быть программа, а не какой-то синтаксис, поддерживаемый вашей оболочкой напрямую. Поддержка синтаксиса без пробелов была бы возможна для ksh
, но она не работала бы в системах с внешним [[
, поэтому по соображениям совместимости лучше сохранить требуемое пространство.
BusyBox предоставляет [[
как внешнюю команду , например.
Важно отметить, что [
является командой, а ключевое слово 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
- это то, что есть: ничего, что оболочка не понимает, поэтому она предполагает, что это ее собственная команда. ;-)
[[-e
- это потенциально допустимое (хотя и странное) имя команды.
– Greg Hewgill
23.06.2019, 09:01
char
является ключевым словом, но вы все же i> не можете написатьcharsomeletter = 'A';
и ожидать, что парсер остановится после просмотраchar
. – Community 23.06.2019, 17:44i--<=++j
, и у компилятора нет проблем с его разбором какi -- <= ++ j
. – Greg Hewgill 24.06.2019, 05:05_
). Ksh имеет(
в качестве оператора i>, а(echo hello)
анализирует как( echo hello )
. Он имеетif
,{
,[[
и другие ключевые слова i>, а такжеifx
,{x
и[[x
- каждый токен, какcharsomeletter
один токен C. Напротив, без кавычек(x
в ksh есть два токена - например, какi--
- два токена в C. То, что концептуально даже означает i> быть оператором, различается между оболочками в стиле Борна ksh) и C. Но Питер А. Шнайдер указал на реальное лексическое i> сходство. – Greg Hewgill 24.06.2019, 08:13