Kann ich Variablen innerhalb der {} Erweiterung ohne `eval` verwenden?

7

Kann ich Variablen innerhalb der {}Erweiterung verwenden, ohne sie anzusprechen eval? Wenn das so ist, wie?

Das funktioniert nicht:

$ touch 1.foo 1.bar
$ ls 1.{foo,bar}
1.bar  1.foo
$ extensions=foo,bar
$ ls 1.{$extensions}
ls: cannot access 1.{foo,bar}: No such file or directory

Es funktioniert mit eval:

$ eval ls 1.{$extensions}
1.bar  1.foo
nr
quelle
3
Die Variablenerweiterung erfolgt nach der Worterweiterung, sodass {$ ...} ein einzelnes Wort ist. 'tho {$ foo, $ bar} würde funktionieren.
Ricky Beam

Antworten:

9

Die Erweiterung der Zahnspange erfolgt sehr früh während der Erweiterung (tatsächlich als erstes), bevor die Variable erweitert wird. Um eine Klammererweiterung für das Ergebnis einer variablen Erweiterung durchzuführen, müssen Sie verwenden eval.

Sie können ohne die gleiche Wirkung erzielen , evalwenn Sie machen extensionsein Wildcard - Muster statt einer Klammer Muster. Stellen Sie die extglobOption zum Aktivieren von ksh-ähnlichen Mustern ein .

shopt -s extglob
extensions='@(foo|bar)'
ls 1.$extensions
Gilles 'SO - hör auf böse zu sein'
quelle
3

Hier ist eine Möglichkeit, Variablen in geschweiften Klammern ohne Auswertung zu erweitern :

end=3
declare -a 'range=({'"1..$end"'})'

Wir haben jetzt eine schöne Reihe von Zahlen:

for i in ${range[@]};do echo $i;done
1
2
3

Getestet in Bash 4.3.11, sollte aber in allen modernen Versionen funktionieren.

Jonathan Cross
quelle
Nur eine Syntax, die ich verwendet habe: read line; declare -a "l=($line)"; echo ${l[@]}- Ich habe diese platziert, damit ein Benutzer erweiterte Klammern eingeben und das Ergebnis anzeigen kann (in einer Art Bash-Tutorial).
Gluk47
1
In der Tat ist evaldas nicht weniger gefährlich als eval. Mit end='$(reboot)', rebootist genau das gleiche laufen.
Stéphane Chazelas
2

Eine andere Technik, die Sie in Bash und einigen anderen Shells verwenden können, ist die Verwendung eines Arrays:

$ extensions=(foo bar)
$ ls "${extensions[@]/#/1.}"

Dies ist die (Mustersubstitutions-) Form der Parametererweiterung. (Leider ist dies auch nicht POSIX-kompatibel.) Das gibt an, dass die Substitution auf jedes Element des Arrays angewendet werden soll und dass die Trennung zwischen den Elementen beibehalten werden soll.  in einem Muster wirkt die Zeichenfolge wie ein regulärer Ausdruck (dh die meisten Substitutionen); Dies bedeutet, dass die Ersetzung nur am Anfang der Parameterwerte erfolgen sollte. Damit${parameter/pattern/string}[@]#^s/old/new/

$ ls "${extensions[@]/#/1.}"

ist äquivalent zu

$ ls "$ {extensions [ 0 ] / # / 1.}" "$ {extensions [ 1 ] / # / 1.}"

(da dieses Array zwei Elemente hat, die als 0 und 1 indiziert sind) und dies erweitert sich auf

$ ls "1.foo" "1.bar"

Zitieren

Die Anführungszeichen sind wichtig, wenn für die „Erweiterungen“ Anführungszeichen erforderlich sind, dh wenn sie (möglicherweise jemals) Leerzeichen oder Zeichen zur Erweiterung des Pfadnamens (Glob / Wildcard) enthalten. Zum Beispiel wenn

$ extensions=("foo bar" "*r")

dann

$ ls ${extensions[@]/#/1.}

(ohne Anführungszeichen) würde auf erweitern

$ ls 1.foo bar 1.*r

(in denen 1.foound barsind separate Argumente), und dies wiederum erweitert sich auf

$ ls 1.foo bar 1.anteater 1.bar 1.bear 1.cougar 1.deer 1.grasshopper 

(weil 1.*res sich um einen nicht zitierten Platzhalter handelt). Weitere Informationen zur Wichtigkeit des Zitierens finden Sie unter Auswirkungen auf die Sicherheit, wenn Sie vergessen, eine Variable in Bash / POSIX-Shells zu zitieren .

Sie können das Ende einer Zeichenfolge auch mit %dem folgenden Muster abgleichen :

$ fnames=(cat dog)
$ ls "${fnames[@]/%/.c}"

erweitert sich auf

$ ls "cat.c" "dog.c"

Aber Vorsicht: Funktioniert % nicht wie $in regulären Ausdrücken. Sie müssen es am Anfang des Musters einfügen, um die Übereinstimmung am Ende des Parameterwerts einzuschränken. Zum Beispiel, wenn Sie haben

$ fnames=(cat.c dog.c)

und Sie bekommen cat.ound dog.o, nicht tun "${fnames[@]/.c%/.o}" - es wird nicht funktionieren. Tun

$ ls "${fnames[@]/%.c/.o}"

Aber tun Sie es auch nicht "${fnames[@]/.c/.o}"(lassen Sie das %Ganze weg) - wenn einer der „fnames“ ist dog.catcher.c, wird er in konvertiert (da der erste durch ersetzt wird ).dog.oatcher.c .c.o

Leider gibt es keine einfache Möglichkeit, ein Präfix und ein Suffix hinzuzufügen, wie Sie es könnten

$ ls 1.{foo,bar}.c

Weitere Informationen zu Variablen, Arrays und den Transformationen, die Sie auf sie anwenden können, finden Sie in der Bash-Dokumentation.

G-Man sagt "Reinstate Monica"
quelle