Kiedy {a, b, c} jest rozwijane w bashu, a kiedy nie?

13

Skrypt bash, który zawiera

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

odciski

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

po wykonaniu. Tego się spodziewałem - w miarę {a,b}rozszerzania konstrukcji.

Jednak gdy (inny) skrypt zawiera

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

drukuje

{a,b}-{1,2}

czego się nie spodziewałem. Spodziewałem się, że to wydrukuje a-1 a-2 b-1 b-2. Oczywiście {a,b}konstrukcja nie jest rozwinięta.

Mogę tak rozwinąć

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

Na podstawie tych obserwacji mam dwa pytania: 1) kiedy {a,b}rozwinięto konstrukcję? 2) Czy $(echo {a,b}-{1,2})preferowany sposób uruchomienia rozszerzenia jest wymagany?

René Nyffenegger
źródło
1
Jest to co najmniej zgodne z faktem, że nie można utworzyć zmiennej tablicowej za pomocą prostego =. Na przykład v=a-1 a-2nie będzie działać.
grochmal
@grochmal - to dlatego, że przypisujesz wartość skalarną. v=a-1 a-2oznacza assign 'a-1' to variable v and run 'a-2' v=(a-1 a-2)przypisuje tablicę do zmiennej v. v+=(b-1 b-2)dołącza się do tego.
cas

Odpowiedzi:

15

Podręcznik Bash mówi, że:

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.

Rozszerzenie nawiasu klamrowego nie znajduje się na liście, więc nie jest wykonywane dla przypisania v={a,b}-{1,2}. Jak wspomniano w @Wildcard, proste rozszerzenie do i tak v=a-1 v=b-1 ...byłoby bez sensu.

Ponadto podczas wykonywania echo $vobowiązują następujące zasady:

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.

Rozwinięcie nawiasów następuje przed rozwinięciem zmiennej, więc przypisane nawiasy klamrowe $vnie są rozwijane.

Ale możesz robić takie rzeczy:

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

Rozwinięcie go za pomocą $(echo ...)powinno działać, jeśli nie ma żadnych białych znaków w ciągu, który ma zostać rozwinięty, a zatem nie będzie miał problemów z dzieleniem słów. Lepszym sposobem może być użycie zmiennej tablicowej, jeśli możesz.

np. zapisz rozwinięcie do tablicy i uruchom polecenie z rozszerzonymi wartościami:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
ilkkachu
źródło
5

Ciekawy punkt. Prawdopodobnie pomocny jest następujący fragment man bash:

Zmienna może być przypisana do oświadczeniem formularza

      name = [ wartość ]

Jeśli wartość nie zostanie podana, do zmiennej zostanie przypisany ciąg zerowy. Wszystkie wartości podlegają interpretacji tyldy, interpretacji parametrów i zmiennych, podstawianiu poleceń, interpretacji arytmetycznej i usuwaniu cudzysłowów (patrz EKSPANSJA poniżej).

Zauważ, że rozwinięcie nawiasu NIE jest wymienione na liście.

Pozostaje jednak pytanie, a mianowicie: Skąd powłoka wie, że jest to zmienne przypisanie, a zatem nie podlega rozszerzaniu nawiasów? A dokładniej, gdzie sekwencja analizowania jest wyjaśniona i skodyfikowana tak, że powłoka jest zdefiniowana w celu identyfikacji przypisań zmiennych, zanim zajmie się rozszerzaniem nawiasów? (Tak oczywiście bashdziała, ale nie znalazłem dokładnej linii dokumentacji opisującej to.)

Dzika karta
źródło
1
Może to ? „2. Słowa, które nie są przypisaniami ani przekierowaniami zmiennych, są rozwijane (patrz Rozszerzenia powłoki).”
Jeff Schaller
@JeffSchaller, masz rację. W rzeczywistości najlepiej odpowiedzieć na to pytanie, czytając sekcję „PROSTE ROZSZERZENIE POLECENIA” ( LESS=+/SIMPLE man bash).
Wildcard,
0

zgodnie z moją wiedzą, {a, b, c} jest rozszerzane, gdy jest bezpośrednio odbijane echem lub używane z poleceniem np .: mkdir ~ / {a, b, c}, ale kiedy jest ustawione na zmienną, powinno zostać ocenione przed odbijanie go lub używanie go jako argumentu!

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

ponieważ masz „a”, po którym następuje „b” w kolejności alfa [az], i „1”, a następnie „2” w kolejności decyzyjnej [0–9]: możesz użyć podwójnej kropki „..” insteed przecinku, „

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
Jonasz
źródło
1
Pytanie brzmi, dlaczego vnie ustawiono na literalny ciąg „a-1 a-2 b-1 b-2”. A przynajmniej dlaczego nie pojawił się żaden błąd, gdy się v=a-1 v=a-2 v=b-1 v=b-2pojawi, jeśli wpiszesz polecenie: (do którego można by się rozszerzyć przypisania zmiennej). To dobre pytanie; to tak naprawdę nie odpowiada.
Wildcard,
@SatoKatsura, co oznacza „rozszerzenie wieloznaczne”? Istnieje rozwinięcie parametrów, rozwinięcie nazwy ścieżki i rozwinięcie nawiasu klamrowego - każdy, do którego możesz się odwoływać i które są różne. Czy miałeś na myśli dzielenie słów?
Wildcard,
0

Przypisanie zmiennej do bash nie rozwija wyrażenia.

Dla tego małego skryptu x będzie zawierać, "*"a nie rozszerzenie "*":

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

Jednak niektóre wartości są rozszerzone, zob. ładna odpowiedź od ilkkachu.

Wyrażenia są rozszerzane podczas ich oceny.

Lubię to:

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

Lub tak:

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

Lub tak:

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

Wyzwalanie $()jest dość powszechne i myślę, że jest preferowane w stosunku do eval, ale najlepsze jest prawdopodobnie wcale nie wyzwalanie oceny, dopóki zmienna nie zostanie faktycznie użyta w poleceniu.

Alexander
źródło