Wann brauchen wir geschweifte Klammern um Shell-Variablen?

657

Wann verwenden wir in Shell-Skripten? {} Erweitern von Variablen?

Zum Beispiel habe ich Folgendes gesehen:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Gibt es einen signifikanten Unterschied oder ist es nur Stil? Wird einer dem anderen vorgezogen?

Neuer Benutzer
quelle

Antworten:

749

In diesem Beispiel macht es keinen Unterschied. Die {}in ${}sind jedoch nützlich, wenn Sie die Variable fooin der Zeichenfolge erweitern möchten

"${foo}bar"

da "$foobar"würde stattdessen die durch identifizierte Variable erweiternfoobar .

Geschweifte Klammern sind auch unbedingt erforderlich, wenn:

  • Array-Elemente erweitern, wie in ${array[42]}
  • Verwenden von Parametererweiterungsoperationen wie in ${filename%.*}(Erweiterung entfernen)
  • Erweiterung der Positionsparameter über 9 hinaus: "$8 $9 ${10} ${11}"

Dies überall zu tun, anstatt nur in möglicherweise mehrdeutigen Fällen, kann als gute Programmierpraxis angesehen werden. Dies dient sowohl der Konsistenz als auch der Vermeidung von Überraschungen $foo_$bar.jpg, bei denen es visuell nicht offensichtlich ist, dass der Unterstrich Teil des Variablennamens wird.

Fred Foo
quelle
106
{}ist als Klammererweiterung bekannt . ${}ist als variable Expansion bekannt. Sie machen verschiedene Dinge. Ich würde dich bis auf das No-Expansion-Bit positiv bewerten.
Spencer Rathbun
5
@NewUser "Abgesehen von Arrays ist dies nicht unbedingt erforderlich. " Nicht so, die geschweiften Klammern sind für die PARAMETER-ERWEITERUNG erforderlich , ein sehr nützliches Konstrukt für die Skripterstellung . Ich habe viele sed- und awk-Skripte gesehen, die durch eine gewisse Parametererweiterung ersetzt werden können.
SiegeX
10
@caffinatedmonkey $()wird verwendet, um einen Befehl auszuführen, der md5sum=$(md5sum foo.bin)die Ausgabe von md5sum foo.binin der Variablen speichert md5sum, auf die jetzt mit zugegriffen werden kann ${md5sum}. Außerdem +1 und viele mehr im Geiste von OP für die Erwähnung, dass es eine gute Praxis ist, explizit zu sein!
L0j1k
11
@ L0j1k Apropos explizit, ich finde es wichtig zu erwähnen, dass $()der Befehl von einer Subshell ausgeführt wird .
Adrian Günter
2
@karatedog ${1:-20}ist eine Form der Parametererweiterung. Hier ist es nicht offensichtlich, weil es hauptsächlich Ziffern und arithmetische Operatoren verwendet, die uns täuschen, dass es sich um Arithmetik handelt, aber es bezieht sich tatsächlich auf den Positionsparameter $1, der, wenn er nicht definiert ist, durch einen Standardwert ersetzt wird 20(die Syntax ist ${variable:-default_value}).
Aaron
126

Variablen werden ohne $und ohne deklariert und zugewiesen {}. Du musst benutzen

var=10

zuweisen. Um aus der Variablen zu lesen (mit anderen Worten, erweitern Sie die Variable), müssen Sie verwenden $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Das hat mich manchmal verwirrt - in anderen Sprachen beziehen wir uns auf dieselbe Weise auf die Variable, unabhängig davon, ob sie sich links oder rechts von einer Zuweisung befindet. Aber Shell-Scripting ist anders, $var=10macht nicht das, was Sie vielleicht denken!

Aaron McDaid
quelle
34

Sie verwenden {}für die Gruppierung. Die geschweiften Klammern werden benötigt, um Array-Elemente zu dereferenzieren. Beispiel:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
Glenn Jackman
quelle
Ich konnte die erste Zeile nicht verstehen dir=(*). Soweit ich weiß, dirhandelt es sich um einen integrierten Befehl zum Auflisten von Verzeichnisinhalten (entspricht ls -C -b). Könntest du bitte erklären?
Jarvis
1
Bei der Shell-Programmierung müssen Befehle und Argumente durch Leerzeichen voneinander getrennt werden. Hier sehen Sie das Gleichheitszeichen ohne Leerzeichen, was bedeutet, dass dies eine variable Zuweisung ist. dirist der Name der Variablen, und die Klammern werden verwendet, um die Dateinamenerweiterung *in einem Array zu sammeln .
Glenn Jackman
27

Sie können auch Textmanipulationen in geschweiften Klammern vornehmen:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Ergebnis:

./folder/subfolder/file.txt ./folder

oder

STRING="This is a string"
echo ${STRING// /_}

Ergebnis:

This_is_a_string

Sie haben Recht, "reguläre Variablen" werden nicht benötigt ... Aber es ist hilfreicher für das Debuggen und das Lesen eines Skripts.

SierraX
quelle
11

Das Ende des Variablennamens wird normalerweise durch ein Leerzeichen oder eine neue Zeile gekennzeichnet. Aber was ist, wenn wir nach dem Drucken des Variablenwerts kein Leerzeichen oder keine neue Zeile möchten? Die geschweiften Klammern teilen dem Shell-Interpreter mit, wo sich das Ende des Variablennamens befindet.

Klassisches Beispiel 1) - Shell-Variable ohne nachfolgendes Leerzeichen

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Beispiel 2) Java-Klassenpfad mit versionierten Jars

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(Freds Antwort besagt dies bereits, aber sein Beispiel ist etwas zu abstrakt.)

Sridhar Sarnobat
quelle
5

Geschweifte Klammern werden immer benötigt, um auf Array-Elemente zuzugreifen und die Klammererweiterung durchzuführen.

Es ist gut, nicht zu vorsichtig zu sein und {}für die Erweiterung von Shell-Variablen zu verwenden, auch wenn kein Raum für Mehrdeutigkeiten besteht.

Zum Beispiel:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Es ist also besser, die drei Zeilen wie folgt zu schreiben:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

das ist definitiv besser lesbar.

Da ein Variablenname nicht mit einer Ziffer beginnen kann, ist Shell nicht brauchen {}um nummerierte Variablen (wie $1, $2etc.) , sofern eine solche Expansion durch eine Ziffer folgt. Das ist zu subtil und macht es {}in solchen Kontexten explizit zu verwenden :

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Sehen:

Codeforester
quelle
1
It's good to be not over-cautious: Ich frage mich, was die meisten Leute denken. Verwenden Sie immer geschweifte Klammern, damit Sie sie nicht vergessen, wenn sie benötigt werden, oder verwenden Sie sie nur bei Bedarf, um die Lesbarkeit zu verbessern.
Roger Dahl
1
Ich denke, es ist das mangelnde Bewusstsein, das dazu führt, dass Programmierer Curlies verwenden, auch wenn sie nicht benötigt werden. Diese Unwissenheit ähnelt dem anderen häufigen Fehler, keine doppelten Anführungszeichen zu verwenden, um ein versehentliches Teilen oder Globbing von Wörtern zu verhindern. Die Realität ist, dass Programmierer Shell-Skripte nicht so ernst nehmen wie andere Skriptsprachen wie Python und Ruby.
Codeforester
1
Stimmt das. Mein Haustier ärgert sich, dass jeder zu denken scheint, dass alle Variablen alle Großbuchstaben in Shell-Skripten sein sollten :)
Roger Dahl
2

Gemäß dem Vorschlag von SierraX und Peter zur Textmanipulation werden geschweifte Klammern {}verwendet, um eine Variable an einen Befehl zu übergeben, zum Beispiel:

Angenommen , Sie haben eine sposi.txt- Datei, die die erste Zeile eines bekannten italienischen Romans enthält:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ausgabe: quel ramo del lago di como che volge a mezzogiorno

Erstellen Sie nun zwei Variablen:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Jetzt ersetzen Sie den Wort variable Inhalte mit dem von new_word , innerhalb sposi.txt Datei

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ausgabe: quel filone del lago di como che volge a mezzogiorno

Das Wort "Ramo" wurde ersetzt.

Fabio Natalini
quelle
1
Dies funktioniert genauso gut ohne geschweifte Klammern um die Variablen.
Armali
Möglicherweise möchten Sie das weel-known novelBit reparieren . Trotzdem positiv bewertet.
gsl