Unterschied zwischen $ {} und $ () im Shell-Skript

Antworten:

39

$(command)ist "Befehlsersetzung". Wie Sie zu verstehen scheinen, führt es das aus command, erfasst die Ausgabe und fügt sie in die Befehlszeile ein, die das enthält $(…). z.B,

$ ls -ld $(date +%B).txt
-rwxr-xr-x  1 Noob Noob    867 Jul  2 11:09 July.txt

${parameter}ist "Parametersubstitution". Viele Informationen finden Sie in der Manpage der Shell, bash (1) , unter der Überschrift „ Parameter Expansion “:

${parameter}
    Der Wert von parameter wird ersetzt. Die geschweiften Klammern sind erforderlich, wenn parameter ein Positionsparameter mit mehr als einer Ziffer ist oder wenn auf parameter ein Zeichen folgt, das nicht als Teil seines Namens interpretiert werden soll.

Informationen zu Positionsparametern finden Sie weiter unten unter „ Positionsparameter “. Am häufigsten wird, wie in den anderen Antworten gezeigt, parameterein Variablenname verwendet. Mit dem ${…}am Ende des obigen Abschnitts angegebenen Formular können Sie den Wert einer Variablen (dh ) abrufen und dieser unmittelbar einen Buchstaben, eine Ziffer oder einen Unterstrich hinzufügen:$variable_name

$ animal = cat
$ Echo $ Tiere
                                # Keine Variable wie "Tiere".
$ echo $ {animal} s
Katzen
$ echo $ animal_food
                                # Keine Variable wie "animal_food".
$ echo $ {animal} _food
Katzenfutter

Sie können dies auch mit Anführungszeichen tun:

$ echo "$ animal" s
Katzen

Als Übung zu Optionen können Sie auch eine zweite Variable verwenden:

$ plural = s
$ echo $ animal $ plural
Katzen

Dies ist jedoch nur Schritt 1. Der nächste Absatz in der Manpage ist interessant, wenn auch etwas kryptisch:

Wenn das erste Zeichen des Parameters ein Ausrufezeichen ( !) ist, wird eine variable Indirektionsebene eingeführt. Bash verwendet den Wert der aus dem Rest des Parameters gebildeten Variablen als Namen der Variablen. Diese Variable wird dann erweitert und dieser Wert wird im Rest der Ersetzung verwendet, anstatt der Wert des Parameters selbst. Dies wird als indirekte Expansion bezeichnet .     (Ausnahmen)    Das Ausrufezeichen muss unmittelbar nach der linken Klammer stehen, um die Indirektion einzuführen.

Ich bin mir nicht sicher, wie ich das klarer machen kann, außer am Beispiel:

$ animal = cat
$ Echo $ Tier
Katze
$ cat = tabby
$ echo $ cat
tabby
$ echo $ {! animal}
Tabby                            # Wenn $ Tier ist „cat“ , dann $ {!} Tier ist $ cat , das heißt, „Tabby“

Nennen wir diesen Schritt 1½. Es gibt viele interessante Dinge, die Sie als Schritt 2 tun können:

$ animal = cat
$ echo $ {# animal}
3                                # Stringlänge
$ echo $ {animal / at / ow}
Kuh                              # Substitution

Sie können nichts ohne die {... }Klammern tun .

Positionsparameter

Betrachten Sie dieses künstliche Beispiel:

$ cat myecho.sh
echo $ 1 $ 2 $ 3 $ 4 $ 5 $ 6 $ 7 $ 8 $ 9 $ 10 $ 11 $ 12 $ 13 $ 14 $ 15
$ ./myecho.sh Hey diddle diddle, Die Katze und die Geige, Die Kuh sprang über den Mond.
Hey diddle diddle, Die Katze und die Geige, Die Hey0 Hey1 Hey2 Hey3 Hey4 Hey5

weil die Shell nicht versteht $10, $11usw. Es behandelt, $10als ob es wäre ${1}0. Aber es versteht ${10}, ${11}usw., wie in der Manpage erwähnt ("ein Positionsparameter mit mehr als einer Ziffer").

Aber schreibe keine solchen Skripte. Es gibt bessere Möglichkeiten, mit langen Argumentlisten umzugehen.

Das Obige (zusammen mit vielen weiteren Formen von Konstrukten) wird ausführlicher in der Manpage der Shell, bash (1), behandelt .${parameter…something_else}

Eine Anmerkung zu Anführungszeichen

Beachten Sie, dass Sie Shell-Variablen immer in Anführungszeichen setzen sollten, es sei denn, Sie haben einen guten Grund, dies nicht zu tun , und Sie sind sicher , dass Sie wissen, was Sie tun. Während geschweifte Klammern wichtig sein können, sind sie im Gegensatz dazu nicht so wichtig wie Anführungszeichen.

$ filename = "Kinderreim.txt"
$ ls -ld $ {filename}
ls: kann nicht auf den Kindergarten zugreifen: Keine solche Datei oder kein solches Verzeichnis
ls: kann nicht auf rhyme.txt zugreifen: Keine solche Datei oder kein solches Verzeichnis
$ ls -ld "$ filename"
-rwxr-xr-x 1 Noob Noob 5309 2. Juli 11:09 Kinderreim.txt

Dies gilt auch für Positionsparameter (z. B. "$1"Befehlszeilenargumente) und auch für die Befehlssubstitution:

$ ls -ld $ (Datum "+% B% Y"). txt
ls: Kein Zugriff auf July: Keine solche Datei oder kein solches Verzeichnis
ls: kann nicht auf 2015.txt zugreifen: Keine solche Datei oder Verzeichnis
$ ls -ld "$ (Datum" +% B% Y "). txt"
-rwxr-xr-x 1 Noob Noob 687 Jul 2 11:09 July 2015.txt

Eine kurze Abhandlung über die Interaktion zwischen Anführungszeichen und ... finden Sie unter Bash-Anführungszeichen ohne Befehlsersetzung .$()

G-Man sagt, "Monica wiedereinsetzen"
quelle
Danke für das wundervolle Beispiel. Können Sie die Verwendung von! in deinem Beispiel. Ich verstehe nicht wirklich, wie es funktioniert.
Noob
@Noob: OK, ich habe die Verwendung von ausgearbeitet !.
G-Man sagt, dass Monica
Vielen Dank. Irgendwie bezieht sich animal tatsächlich auf die Variable cat anstatt auf den Wert cat. Kannst du mich auch wissen lassen, wie / at ein "c" in echo wird? $ {Animal / at / ow} Die Mannerklärung von bash ist irgendwie ... ich weiß es nicht. schwer zu verstehen.
Noob
Können Sie auch diesen Satz erläutern: "$ {parameter} Der Wert von parameter wird ersetzt." ersetzt durch was? Wenn der Parameter fruit ist und der Wert apple - fruit = apple ist, wird $ {fruit} - apple durch ?? ersetzt. Hier wird die Bedeutung nicht wirklich ersetzt
Noob
@Noob: " ${!animal}bezieht sich eigentlich auf die Variable $catanstatt auf den Wert cat." Ja, das ist genau der Punkt. "Wie wird / at ein" c "in echo $ {animal / at / ow}?" Huh? / at wird nicht "c"; "Cat" wird "cow", wenn "at" durch "ow" ersetzt wird.
G-Man sagt, dass Monica
7

In Ihrem Beispiel sind $ var und $ {var} identisch. Geschweifte Klammern sind jedoch nützlich, wenn Sie die Variable in einer Zeichenfolge erweitern möchten:

    $ string=foo
    $ echo ${string}bar
      foobar
    $ echo $stringbar

    $ 

Somit bieten die geschweiften Klammern eine Möglichkeit, die Variable zu ersetzen, um den Namen der neuen Variablen zu erhalten, die selbst ersetzt werden soll.

MariusMatutiae
quelle
4

Ich sehe es normalerweise häufiger in Streichern. So etwas funktioniert nicht:

var="a"
echo "$varRAW_STRING"

Aber das wird:

var="a"
echo "${var}RAW_STRING"

Wie Sie richtig sagten, $()wird verwendet, um einen Befehl auszuführen:

dir_contents=$(ls)

Sie können auch Backticks verwenden, aber ich finde die $()vielseitiger. Zum einen können Backticks nicht (einfach) verschachtelt werden.

date_directory=`ls `date '+%Y-%m-%d'`` # Makes no sense
date_directory=$(ls $(date '+%Y-%m-%d')) # Much better
bytesized
quelle
Eigentlich Backticks können seine verschachtelten: date_directory=`ls \`date '+%Y-%m-%d'\``. Aber es ist schrecklich hässlich. $(…)ist viel klarer und einfacher zu bedienen.
G-Man sagt, dass Monica
OK Fair genug. Es ist technisch möglich, aber ich kann mir nicht vorstellen, dass es jemals jemand tut, nur wegen der Lesbarkeit
bytesized
1
Nun, `…`war die (einzige) Syntax für die Befehlsersetzung vor Jahren $(…)erfunden worden, so müssen Sie sich nichts vorstellen - die Leute haben es getan.
G-Man sagt, dass Monica
s / ever doing it / doing it
more