Wann wird {a, b, c} in bash erweitert, wann nicht?

13

Ein Bash-Skript, das enthält

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

druckt

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

wenn ausgeführt. Dies ist, was ich erwartet hatte - als das {a,b}Konstrukt erweitert wird.

Wenn jedoch (ein anderes) Skript enthält

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

es druckt

{a,b}-{1,2}

Das ist nicht das, was ich erwartet hatte. Ich habe erwartet, dass es gedruckt wird a-1 a-2 b-1 b-2. Offensichtlich ist das {a,b}Konstrukt nicht erweitert.

Ich kann es so erweitern

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

Aufgrund dieser Beobachtungen habe ich zwei Fragen: 1) Wann wird das {a,b}Konstrukt erweitert? 2) Ist $(echo {a,b}-{1,2})die bevorzugte Methode, um bei Bedarf eine Erweiterung auszulösen?

René Nyffenegger
quelle
1
Dies steht zumindest im Einklang mit der Tatsache, dass Sie mit einem einfachen Array keine Variable erstellen können =. Zum Beispiel v=a-1 a-2wird nicht funktionieren.
Grochmal
@grochmal - das liegt daran, dass Sie einen Skalarwert zuweisen. v=a-1 a-2means assign 'a-1' to variable v and run 'a-2' v=(a-1 a-2)weist das Array der Variablen zu v. v+=(b-1 b-2)hängt daran an.
cas

Antworten:

15

Das Bash-Handbuch besagt Folgendes:

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.

Die Klammererweiterung ist nicht in der Liste enthalten, daher wird sie für die Zuweisung nicht ausgeführt v={a,b}-{1,2}. Wie von @Wildcard erwähnt, wäre die einfache Erweiterung v=a-1 v=b-1 ...um sowieso sinnlos.

Bei der Ausführung von echo $vgilt außerdem Folgendes:

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.

Die Klammererweiterung erfolgt vor der variablen Erweiterung, sodass die zugewiesenen Klammern $vnicht erweitert werden.

Aber Sie können so etwas tun:

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

Das Erweitern mit $(echo ...)sollte funktionieren, wenn die zu erweiternde Zeichenfolge kein Leerzeichen enthält und daher keine Probleme mit der Wortteilung auftreten. Ein besserer Weg könnte sein, eine Array-Variable zu verwenden, wenn Sie können.

Speichern Sie z. B. die Erweiterung in einem Array und führen Sie einen Befehl mit den erweiterten Werten aus:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
ilkkachu
quelle
5

Ein interessanter Punkt. Möglicherweise hilfreich ist der folgende Auszug aus man bash:

Eine Variable kann durch eine Anweisung des Formulars zugewiesen werden

      name = [ value ]

Wenn kein Wert angegeben wird, wird der Variablen die Nullzeichenfolge zugewiesen. Alle Werte werden einer Tilde-Erweiterung, einer Parameter- und Variablenerweiterung, einer Befehlssubstitution, einer arithmetischen Erweiterung und einer Anführungszeichenentfernung unterzogen (siehe ERWEITERUNG unten).

Beachten Sie, dass die Klammererweiterung NICHT in der Liste aufgeführt ist.

Es bleibt jedoch die Frage: Woher weiß die Shell, dass es sich um eine variable Zuweisung handelt, die also keiner Klammererweiterung unterliegt? Oder genauer gesagt, wo wird die Analysesequenz so geklärt und kodiert, dass die Shell definiert wird, um Variablenzuweisungen zu identifizieren, bevor sie die Klammererweiterung ausführt? (Dies bashfunktioniert natürlich so , aber ich habe nicht die genaue Dokumentationsreihe gefunden, die dies beschreibt.)

Platzhalter
quelle
1
Vielleicht das ? "2. Die Wörter, bei denen es sich nicht um variable Zuweisungen oder Umleitungen handelt, werden erweitert (siehe Shell-Erweiterungen)."
Jeff Schaller
@ JeffSchaller, du bist richtig. Eigentlich lässt sich diese Frage am besten durch Lesen des Abschnitts "EINFACHE BEFEHLSERWEITERUNG" ( LESS=+/SIMPLE man bash) beantworten .
Wildcard
0

Meines Wissens nach wird {a, b, c} erweitert, wenn es direkt wiedergegeben oder mit einem Befehl verwendet wird, zB: mkdir ~ / {a, b, c}, aber wenn es auf eine Variable gesetzt ist, sollte es vorher ausgewertet werden es wiedergeben oder als Argument verwenden!

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

da Sie "a" gefolgt von "b" in alphabetischer Reihenfolge [az] und "1" gefolgt von "2" in dezimaler Reihenfolge [0-9] haben: Sie können doppelte Punkte ".." in Komma "verwenden, "

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
Jona
quelle
1
Die Frage ist , warum vwurde nicht gesetzt "a-1 a-2 b-1 b-2" auf die Zeichenkette. Oder zumindest, warum kein Fehler ausgegeben wurde, wenn Sie den Befehl eingeben: v=a-1 v=a-2 v=b-1 v=b-2(auf den die Variablenzuweisung erweitert werden sollte). Das ist eine gute Frage. das beantwortet es nicht wirklich.
Wildcard
@SatoKatsura, was soll "Wildcard-Erweiterung" bedeuten? Es gibt Parametererweiterungen, Pfadnamenerweiterungen und Klammererweiterungen, auf die Sie sich möglicherweise beziehen und die alle unterschiedlich sind. Oder meintest du vielleicht Wortspaltung?
Wildcard
0

Das Zuweisen einer Variablen in bash erweitert den Ausdruck nicht.

Für dieses kleine Skript enthält x "*"und nicht die Erweiterung von "*":

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

Einige Werte sind jedoch erweitert, vgl. die nette antwort von ilkkachu.

Ausdrücke werden bei der Auswertung erweitert.

So was:

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

Oder so:

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

Oder so:

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

Das Auslösen $()ist ziemlich häufig und wird meiner Meinung nach bevorzugt eval, aber das Beste ist wahrscheinlich, die Auswertung erst dann auszulösen, wenn die Variable tatsächlich in einem Befehl verwendet wird.

Alexander
quelle