Почему пробелы иногда нужны вокруг метасимволов?

544

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

bash: syntax error near unexpected token `{:'

Вчера это случилось, когда я попытался запустить его в оболочке Bash друга , а затем я добавил пробел, и он неожиданно заработал :(){ :|:& };:вместо:(){:|:&};:

Имеет ли значение пробел; я татуировал ошибку синтаксиса на моей руке ?!

Кажется, всегда работает в Zsh , но не в Bash.

Связанный вопрос не объясняет ничего о пробелах, что действительно является моим вопросом; Почему пробел необходим Bash для правильного его анализа?

spydon
источник
6
Я отправил тот же вопрос здесь (исключая часть татуировки).
Бенуа
3
Кроме того, двоеточие (:) нельзя использовать в качестве имени функции (см. Pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... FreeBSD / bin / sh даже выдает ошибку на этом ...
Мартин Турной
5
@Carpetsmoker: я не уверен, насколько это актуально. Это вопросы о Баше.
Деннис

Ответы:

268

В BASH есть список символов, которые разделяют токены. Эти символы называются метасимволы и они |, &, ;, (, ), <, >, пространство и вкладки . С другой стороны, фигурные скобки ( {и }) - это просто обычные символы, составляющие слова.

Пропуск второго пробела до того, как }будет сделано, так &как является метасимволом. Поэтому ваша татуировка должна иметь хотя бы один космический символ.

:(){ :|:&};:
Дмитрий Чубаров
источник
35
Простое решение: переместите первую часть тау влево и пересадите кожу между двумя частями.
23
Мне понравился термин on the other hand... каламбур? ;): D (Извините, за не по теме комментарий.)
anishsane
4
Но это отличается от Zsh? Чем Zsh отличается?
августа
2
Хорошее объяснение, за исключением того, что {и }являются ключевыми словами оболочки в этом контексте. Только если они не распознаются как таковые - из-за отсутствия окружающих пробелов / метасимволов - они рассматриваются как буквальная часть слова, частью которого они становятся. (И затем есть расширение скобки, которое происходит в другом контексте.)
mklement0
2
@DmitriChubarov В скобках разрешены фигурные скобки (включая функции:. {foo} () { echo hello; }«Имя», определяемые bashкак «слово, состоящее только из буквенно-цифровых символов и подчеркиваний и начинающиеся с буквенного символа или подчеркивания», применяется только к именам переменных.
Чепнер
82

Просто тату

#!/bin/zsh

Шебанг над ним, и все будет хорошо.

SZG
источник
6
Если мы привередливы, шебанг не будет работать вообще, так как оболочка, мы надеемся, zsh, находится в интерактивном режиме, о чем свидетельствует приглашение ...
SzG
50

Скобки больше похожи на нечетные ключевые слова, чем на специальные символы и требуют пробелов. Это отличается от скобок, например. Для сравнения:

(ls)

который работает, и:

{ls}

который ищет команду по имени {ls}. Чтобы работать, это должно быть:

{ ls; }

Точка с запятой останавливает закрывающую скобку в качестве параметра ls.

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

Питер Вестлейк
источник
12
@DmitriChubarov - это очень подлый трюк, использующий совершенно другое значение скобок. Он расширяет список значений через запятую, в данном случае это просто ls.
Питер Уэстлейк
7
Но ... мальчик ... ты будешь делать татуировку на всю оставшуюся жизнь! Вы отметили себя как ботаник на все времена! И тогда вы не могли даже сделать это правильно, прежде чем это сшить? Это два шага, кроме того, чтобы быть всезнайкой ;-) Без обид, приятель, мне просто интересно.
Alfe
14
@ Я пытался это сделать до того, как сделал татуировку, но я попробовал это в моем Zsh, я думал, что он имеет такой же синтаксический анализ, как Bash. Глупо с моей стороны, но я просто скажу людям, что это зш. :)
Спидон
20
@spydon Или вы говорите им, что специально удалили это место, чтобы люди, скопировавшие команду из вашей татуировки, случайно не запустили ее и не разбили свою машину;)
ткните
4
Эй, если это действительно в zsh, почему бы просто не добавить #! / Bin / zsh прямо над ним (или перед ним)? Во всяком случае, лучше сначала указать оболочку.
Адам Миллер
41

Хотя это не так легко увидеть в шрифте татуировки, на самом деле между фигурной скобкой и двоеточием есть знак порядка байтов (BOM) (возможно, вы были достаточно опьянены, когда получили татуировку, которую не заметили, но она действительно есть) , Это оставляет три очевидные возможности:

  1. Вы не смогли ввести спецификацию при расшифровке кода. Результатом является очевидное применение GIGO. Оболочка просто не распознает спецификацию, которой нет в вашей неудачной транскрипции.
  2. Ваша оболочка слишком старая. Он не распознает символы Юникода, поэтому спецификация (и, возможно, все остальные символы Юникода) полностью игнорируется, даже если предполагается, что спецификация в любом месте, кроме начала файла, рассматривается как неразрывный пробел нулевой ширины. ,
  3. Ваша оболочка слишком новая. Использование спецификации в качестве ZWNBS не рекомендуется, и авторы реализовали будущую версию Unicode, в которой такое использование больше не разрешено.
Джерри Гроб
источник
40

а потом я добавил пробел, и это вдруг сработало ...

Это из-за того, как оболочка разбирает. Вам нужен пробел после начала определения функции, то есть после {.

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

действительны. С другой стороны,

foo() {echo hey&}

нет.


Тебе действительно нужна татуировка, подобная этой:

введите описание изображения здесь


Из источника :

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

Пропуск пробела после {причины приводит к {echoтому, что интерпретируется как один токен.


Эквивалентная форма

:(){ :|:& };:

было бы

:(){
:|:& };:

Обратите внимание, что {в альтернативной версии пробела нет , но разрыв строки приводит к тому, что оболочка распознается {как токен.

devnull
источник
«Пропуск пробела после {приводит к тому, что {echo» интерпретируется как один токен ». - так верит ли парсер, что столкнулся с командой, вызванной {echoдо того, как он достиг требуемой открывающей скобки?
Иона