Как bash различает расширение фигурных скобок и группирование команд?

48

Я заметил, что {можно использовать в расширении скобок:

echo {1..8}

или в группировке команд:

{ls;echo hi}

Как Bash узнает разницу?

lovespring
источник
1
Отличный вопрос, +1. Может показаться, что {это интерпретируется как список команд, если он появляется в начале команды, а в противном случае как расширение скобки, но я не уверен.
Селада
16
{ls;echo hi}не является законным bash. Вам нужен пробел после открывающей скобки и точка с запятой перед закрывающей.
PSkocik

Ответы:

39

Упрощенная причина является наличие одного символа: space.

Расширения скобок не обрабатывают (не заключенные в кавычки) пробелы.

{...}Список потребностей (ООН-цитируемый) пространства.

Более подробный ответ - как оболочка анализирует командную строку .


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

  1. Разбивает команду на токены, разделенные фиксированным набором метасимволов: SPACE, TAB, NEWLINE,;, (,), <,>, | и &. Типы токенов включают слова, ключевые слова, перенаправители ввода / вывода и точки с запятой.

Мета-символы: spacetabenter;,<>|а &.

После разбиения слова могут иметь тип (как понимается оболочкой):

  • Предварительные назначения команд: LC=ALL ...
  • команда LC=ALL echo
  • аргументы LC=ALL echo "hello"
  • Перенаправление LC=ALL echo "hello" >&2

Расширение скобки

Только если «строка скобок» (без пробелов или метасимволов) является одним словом (как описано выше) и не заключена в кавычки , она является кандидатом на «Расширение скобок». Больше проверок выполнено на внутренней структуре позже.

Таким образом, это: {ls,-l}квалифицируется как «расширение скобки», чтобы стать ls -l, как first wordили argument(в bash, zsh отличается).

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

Но это не будет {ls ,-l}. Bash разделит spaceи проанализирует строку как два слова: {lsи ,-l}которая вызовет a command not found(аргумент ,-l}потерян):

 $ {ls ,-l}
 bash: {ls: command not found

Ваша строка: {ls;echo hi}не станет «расширением скобок» из-за двух метасимволов ;и space.

Он будет разбит на следующие три части: {lsновая команда: echo hi}. Поймите, что ;запускает начало новой команды. Команда {lsне будет найдена, и следующая команда напечатает hi}:

$ {ls;echo hi}
bash: {ls: command not found
hi}

Если он помещен после какой-либо другой команды, он все равно начнет новую команду после ;:

$ echo {ls;echo hi}
{ls
hi}

Список

Одна из «составных команд» является «Brace List» (мои слова): { list; }.
Как видите, он определяется пробелами и закрытием ;.
Пробелы и ;нужны потому, что оба {и }являются «зарезервированными словами ».

И поэтому, чтобы быть узнаваемым как слова, должны быть окружены метасимволами (почти всегда:) space.

Как описано в пункте 2 связанной страницы

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

Ваш пример: {ls;echo hi}это не список.

Требуется закрытие ;и один пробел (как минимум) после {. Последний }определяется закрытием ;.

Это список { ls;echo hi; }. И это { ls;echo hi;}тоже (менее часто используемый, но действительный) (спасибо @choroba за помощь).

$ { ls;echo hi; }
A-list-of-files
hi

Но в качестве аргумента (оболочка знает разницу) команды, она вызывает ошибку:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

Но будьте осторожны с тем, что, по вашему мнению, интерпретирует оболочка:

$ echo { ls;echo hi;
{ ls
hi

источник
2
это действительно лучший ответ, потому что вы дадите нам, как работает bash-парсер! и с подробным объяснением!
родник
2
Вам не нужно пространство между ;и }. { ls;}работает как точка с запятой уже мета-символ.
Чороба
1
@lovespring Спасибо, да, я потратил некоторое время на его написание. Я рад узнать, что это полезно. Еще раз спасибо.
отличная статья, большое спасибо за ссылки
Эдвард Торвальдс
16

Блок {является ключевым словом оболочки, поэтому он должен отделяться от следующего слова пробелом, в то время как в раскрытии фигурных скобок не должно быть пробела (если вам нужно заключить в скобки расширение пробела, вы должны его избежать:) echo {\ ,a}{b,c}.

Вы можете использовать скобки в начале команды:

{ls,.}  # expands to "ls ."

Вы не можете использовать его для расширения до блока, так как синтаксический анализ команд группировки происходит перед расширениями:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory
choroba
источник
5

Это можно узнать, проверив синтаксис командной строки. Точно так же он знает, что в выражении echo echoпервый эхо-сигнал должен рассматриваться как команда, а второй эхо-сигнал - как параметр первого эхо-сигнала.

В bash это очень просто, так как { cmd; }должны иметь пробелы и точки с запятой. Однако, например, в zsh они не нужны, но все же, анализируя контекст {}оболочки, может сказать, что следует делать с ее содержимым.

Учтите следующее:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

Оба возвращают текущую дату, но

echo {1..3}

возвращается, 1 2 3потому что shell знает {}в аргументе команды echo, поэтому должен быть расширен.

jimmij
источник
{после пробела без кавычек не начинается расширение скобок в bash.
Чороба
@choroba Да, и не только сразу {. Пробел без кавычек не может быть нигде, потому что shell разбивает всю командную строку на пробелы.
Джимми
0

Во-первых, составная фигурная скобка должна представлять собой само слово и первое слово командной строки:

echo { these braces are just words }

Во-вторых, отдельные фигурные скобки не являются специальными (как вы можете видеть выше). Пустые скобки тоже не особенные:

echo {} # just the token {}: familiar from the find command

Все без запятых тоже само по себе

echo {abc} # just {abc}

Здесь начинается действие.

echo {a,b} # braces disappear, a b results.

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

Это может быть первое слово в командной строке, кстати:

{ls,-l} .   # just "ls -l ."
Kaz
источник