Когда {a, b, c} раскрывается в bash, а когда нет?

13

Скрипт bash, который содержит

for i in {a,b}-{1,2}; do
  echo $i;
done

печать

a-1
a-2
b-1
b-2

когда выполнено. Это то, что я ожидал - поскольку {a,b}конструкция расширяется.

Однако когда (другой) скрипт содержит

v={a,b}-{1,2}
echo $v

это печатает

{a,b}-{1,2}

это не то, что я ожидал. Я ожидал, что это напечатает a-1 a-2 b-1 b-2. Очевидно, что {a,b}конструкция не расширена.

Я могу заставить его расширяться так

v=$(echo {a,b}-{1,2})

На основании этих наблюдений у меня есть два вопроса: 1) когда {a,b}расширилась конструкция? 2) является $(echo {a,b}-{1,2})ли предпочтительным способом вызвать расширение при необходимости?

Рене Ниффенеггер
источник
1
Это, по крайней мере, согласуется с тем фактом, что вы не можете сделать переменную массива простой =. Например, v=a-1 a-2не будет работать.
Grochmal
@grochmal - потому что вы присваиваете скалярное значение. v=a-1 a-2означает, assign 'a-1' to variable v and run 'a-2' v=(a-1 a-2)присваивает массив переменной v. v+=(b-1 b-2)добавляет к нему
Cas

Ответы:

15

В руководстве по Bash сказано, что:

SIMPLE COMMAND EXPANSION
When a simple command is executed, the shell performs the following
expansions, assignments, and redirections, from left to right.
[...]
4. The  text  after the = in each variable assignment undergoes tilde
   expansion, parameter expansion, command substitution, arithmetic
   expansion, and quote removal before being assigned to the variable.

Расширение скобок отсутствует в списке, поэтому оно не выполняется для назначения v={a,b}-{1,2}. Как уже упоминалось в @Wildcard, простое расширение в v=a-1 v=b-1 ...любом случае было бы бессмысленным.

Кроме того, при выполнении echo $v, применяется следующее:

EXPANSION
Expansion is performed on the command line after it has been split
into words. [...]

The order of expansions is: brace expansion; tilde expansion, 
parameter and variable expansion, arithmetic expansion, and command
substitution (done in a left-to-right fashion); word splitting; and
pathname expansion.

Расширение фигурных скобок происходит до расширения переменных, поэтому назначенные фигурные скобки $vне расширяются.

Но вы можете сделать что-то вроде этого:

$ var_this=foo var_that=bar
$ echo $var_{this,that}
foo bar

Расширение с помощью $(echo ...)должно работать, если у вас нет пробелов в расширяемой строке, и, следовательно, не возникнут проблемы с разделением слов. Лучшим способом может быть использование переменной массива, если вы можете.

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

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
ilkkachu
источник
5

Интересный момент. Возможно, полезна следующая выдержка из man bash:

Переменной может быть назначено на утверждении формы

      имя = [ значение ]

Если значение не указано, переменной присваивается пустая строка. Все значения подвергаются расширению тильды, расширению параметров и переменных, подстановке команд, расширению арифметики и удалению кавычек (см. РАЗДЕЛЕНИЕ ниже).

Обратите внимание, что расширение скобки НЕ упоминается в списке.

Однако это все еще оставляет вопрос, а именно: как оболочка узнает, что это присвоение переменной и, следовательно, не подлежит расширению скобки? Или, точнее, где последовательность разбора уточняется и кодифицируется таким образом, что оболочка определяется для определения переменных назначения, прежде чем она обрабатывает расширение скобки? (Это, очевидно, как bashработает, но я не нашел точную строку документации, описывающую это.)

Wildcard
источник
1
Может это ? «2. Слова, которые не являются переменными присваиваниями или перенаправлениями, расширены (см. Расширения оболочки)».
Джефф Шаллер
@JeffSchaller, ты прав. На самом деле, на этот вопрос лучше всего ответить, просто прочитав раздел «ПРОСТОЕ РАСШИРЕНИЕ КОМАНДЫ» ( LESS=+/SIMPLE man bash).
Wildcard
0

Насколько мне известно, {a, b, c} раскрывается, когда он напрямую отображается или используется командой, например: mkdir ~ / {a, b, c}, но когда он установлен в переменную, он должен быть оценен до повторяя это или используя это как аргумент!

u@h:~$ echo {a,b}-{1,2}
a-1 a-2 b-1 b-2
u@h:~$ v={a,b}-{1,2}
u@h:~$ echo $v
{a,b}-{1,2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2

так как у вас есть «a», за которым следует «b» в алфавитном порядке [az], и «1», за которым следует «2» в десятичном порядке [0-9]: вы можете использовать двойной период ".." insured of comma ", "

u@h:~$ echo {a..b}-{1..2}
a-1 a-2 b-1 b-2
u@h:~$ v={a..b}-{1..2}
u@h:~$ echo $v
{a..b}-{1..2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2
Ион
источник
1
Вопрос в том, почему vне была задана буквенная строка «a-1 a-2 b-1 b-2». Или, по крайней мере, почему не было выдано никакой ошибки, если вы наберете команду: v=a-1 v=a-2 v=b-1 v=b-2(к которой можно было бы рассчитывать, что назначение переменной будет расширено). Это хороший вопрос; это на самом деле не отвечает.
Wildcard
@SatoKatsura, что должно означать «расширение по шаблону»? Существует расширение параметров, расширение пути и фигурная скобка - все, на что вы могли ссылаться, и все они разные. Или вы имели в виду разделение слов?
Wildcard
0

Присвоение переменной в bash не расширяет выражение.

Для этого небольшого сценария x будет содержать, "*"а не расширение "*":

#!/bin/bash
x=*
echo "$x"

Однако некоторые значения расширены, ref. хороший ответ от илккачу.

Выражения раскрываются при оценке.

Как это:

x=$(echo *)        # <-- evaluation of "*"
echo "$x"

Или вот так:

x=*
echo $x            # <-- evaluation of $x

Или вот так:

x=*
eval echo "$x"     # <-- evaluation of `echo *`

Запуск $()довольно распространен, и я думаю, что он предпочтительнее eval, но лучше всего, вероятно, вообще не запускать оценку, пока переменная не будет фактически использована в команде.

Александр
источник