Як баш розрізняє розширення дужок і групування команд?


48

Я помітив, що {може використовуватися для розширення дужок:

echo {1..8}

або в групуванні команд:

{ls;echo hi}

Як баш знає різницю?


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}кваліфікується як "Розширення Brace", щоб стати ls -lабо як ( first wordабо argumentв bash, zsh відрізняється).

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

Але це не буде: {ls ,-l}. Bash розділить spaceі проаналізує рядок у вигляді двох слів: {lsі ,-l}що призведе до command not found(аргумент ,-l}втрачено):

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

Ваш рядок: {ls;echo hi}не стане "Brace розширенням" через два мета-символи ;та 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 parser! і з детальним поясненням!
lovepring

2
Вам не потрібно пробіл між ;і }. { ls;}працює як крапка з комою - це вже мета-символ.
choroba

1
@lovespring Спасибі, так, я вклав деякий час у його написання. Я радий знати, що це корисно. Ще раз спасибі.

відмінна стаття, велике спасибі за посилання
Едвард Торвальдс

16

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

Ви можете використовувати розширення дужок на початку команди:

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

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

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory

5

Це знає, перевіривши синтаксис командного рядка. Таким же чином відомо, що в виразі echo echoдо першого відлуння слід ставитися як до команди, а до другого - як до параметра першого відлуння.

У bash це дуже просто, оскільки { cmd; }має бути пробіл і крапка з комою. Однак, наприклад, в zsh вони не потрібні, але все ж, проаналізувавши контекст {}оболонки, можна сказати, що слід робити з її вмістом.

Розглянемо наступне:

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

Обидва повертають поточну дату, але

echo {1..3}

повертається, 1 2 3оскільки оболонка знає {}в аргументі для команди echo, тому її слід розширити.


{після чого котирування без котирування не починає розширення дужок у bash.
choroba

@choroba Так, і не тільки відразу {. Простір без котирування не може бути ніде, оскільки оболонка розбиває весь командний рядок на пробіли.
jimmij

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 ."
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.