Kiedy potrzebujemy nawiasów klamrowych wokół zmiennych powłoki?

657

Kiedy w skryptach powłoki używamy do {}rozwijania zmiennych?

Na przykład widziałem:

var=10        # Declare variable

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

Czy jest znacząca różnica, czy to tylko styl? Czy jedno jest lepsze od drugiego?

Nowy użytkownik
źródło

Odpowiedzi:

750

W tym konkretnym przykładzie nie ma znaczenia. Jednak {}in ${}są przydatne, jeśli chcesz rozwinąć zmienną foow ciągu

"${foo}bar"

ponieważ "$foobar"zamiast tego rozwinąłby zmienną identyfikowaną przez foobar.

Nawiasy klamrowe są również bezwarunkowo wymagane, gdy:

  • rozwijanie elementów tablicy, jak w ${array[42]}
  • za pomocą operacji rozszerzania parametrów, jak w ${filename%.*}(usuń rozszerzenie)
  • rozszerzenie parametrów pozycyjnych poza 9: "$8 $9 ${10} ${11}"

Robienie tego wszędzie, zamiast tylko w potencjalnie dwuznacznych przypadkach, można uznać za dobrą praktykę programistyczną. Ma to na celu zarówno spójność, jak i uniknięcie niespodzianek, takich jak $foo_$bar.jpg, gdy wizualnie nie jest oczywiste, że podkreślenie staje się częścią nazwy zmiennej.

Fred Foo
źródło
106
{}jest znany jako rozszerzenie nawiasów klamrowych . ${}jest znany jako rozszerzenie zmiennej. Robią różne rzeczy. Głosowałbym za tobą, z wyjątkiem bitu bez rozszerzenia.
Spencer Rathbun
5
@NewUser „ Więc inaczej niż tablice, tak naprawdę nie jest to wymagane ” Nie tak, nawiasy klamrowe są niezbędne do PARAMETR EXPANSION , bardzo przydatnej konstrukcji w skryptach. Widziałem wiele skryptów sed i awk, które można zastąpić odrobiną rozszerzenia parametrów.
SiegeX
10
@ cffinatedmonkey $()służy do wykonania polecenia, które md5sum=$(md5sum foo.bin)zapisze dane wyjściowe md5sum foo.binw zmiennej md5sum, która jest teraz dostępna przy użyciu ${md5sum}. Ponadto +1 i wiele innych w duchu do OP za wspomnienie, że dobrą praktyką jest wyrażanie się otwarcie!
L0j1k
11
@ L0j1k Mówiąc o jawności, ważne jest, aby wspomnieć, że $()wykonuje swoje polecenie z podpowłoki .
Adrian Günter
2
@karatedog ${1:-20}jest formą rozszerzania parametrów. Nie jest to tutaj oczywiste, ponieważ wykorzystuje głównie cyfry i operatory arytmetyczne, które oszukują nas w myśleniu, że w grę wchodzi arytmetyka, ale w rzeczywistości odnosi się do parametru pozycyjnego $1, który jeśli nie zostanie zdefiniowany, zostanie zastąpiony wartością domyślną 20(składnia jest ${variable:-default_value}).
Aaron
126

Zmienne są deklarowane i przypisywane bez $i bez {}. Musisz użyć

var=10

przypisać. Aby odczytać ze zmiennej (innymi słowy „rozwinąć” zmienną), musisz użyć $.

$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.

To mnie czasami myliło - w innych językach odwołujemy się do zmiennej w ten sam sposób, niezależnie od tego, czy znajduje się po lewej, czy po prawej stronie zadania. Ale skryptowanie powłoki jest inne, $var=10nie robi tego, co mogłoby się wydawać!

Aaron McDaid
źródło
34

Używasz {}do grupowania. Nawiasy klamrowe są wymagane do wyrejestrowania elementów tablicy. Przykład:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
Glenn Jackman
źródło
Nie mogłem zrozumieć pierwszej linii dir=(*). O ile mi wiadomo, dirto wbudowane polecenie do wyświetlania zawartości katalogu (równoważne z ls -C -b). Czy mógłbyś wyjaśnić?
Jarvis
1
W programowaniu powłoki polecenia i argumenty muszą być oddzielone od siebie spacjami. Tutaj widzisz znak równości bez białych znaków, co oznacza, że ​​jest to zmienne przypisanie. dirjest nazwą zmiennej, a nawiasy są używane do gromadzenia rozszerzenia nazwy pliku *w tablicy.
glenn jackman
27

Możesz także wykonywać pewne operacje na tekście wewnątrz nawiasów klamrowych:

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

Wynik:

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

lub

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

Wynik:

This_is_a_string

Masz rację, że „zwykłe zmienne” nie są potrzebne ... Ale to jest bardziej pomocne przy debugowaniu i czytaniu skryptu.

SierraX
źródło
11

Koniec nazwy zmiennej jest zwykle oznaczony spacją lub znakiem nowej linii. Ale co jeśli nie chcemy spacji ani nowego wiersza po wydrukowaniu wartości zmiennej? Nawiasy klamrowe informują interpretera powłoki, gdzie znajduje się koniec nazwy zmiennej.

Klasyczny przykład 1) - zmienna powłoki bez końcowych białych znaków

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"

Przykład 2) Ścieżka klasy Java z wersjonowanymi słojami

# 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

(Odpowiedź Freda już to stwierdza, ale jego przykład jest nieco zbyt abstrakcyjny)

Sridhar Sarnobat
źródło
5

Nawiasy klamrowe są zawsze potrzebne do uzyskiwania dostępu do elementów tablicy i przeprowadzania rozszerzenia nawiasów klamrowych.

Dobrze jest nie być zbyt ostrożnym i używać {}do rozszerzania zmiennych powłoki, nawet jeśli nie ma miejsca na dwuznaczności.

Na przykład:

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

Lepiej więc zapisać trzy linie jako:

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

co jest zdecydowanie bardziej czytelne.

Ponieważ nazwa zmiennej nie może zaczynać się od cyfry, powłoka nie potrzebuje {}około numerowane zmienne (jak $1, $2itd.), Chyba że ekspansja następuje cyfry. To zbyt subtelne i sprawia, że ​​jawnie używa się go {}w takich kontekstach:

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

Widzieć:

codeforester
źródło
1
It's good to be not over-cautious: Zastanawiam się, co myśli większość ludzi. Przez cały czas używaj nawiasów klamrowych, aby nie zapomnieć o nich, gdy są potrzebne, lub używaj ich tylko w razie potrzeby, aby poprawić czytelność.
Roger Dahl
1
Myślę, że to brak świadomości prowadzi do tego, że programiści używają curli nawet wtedy, gdy nie są potrzebni. Ta niewiedza jest podobna do innej częstej pomyłki polegającej na niestosowaniu podwójnych cudzysłowów, aby zapobiec niezamierzonemu podziałowi słów lub globowaniu. U podstaw tego leży fakt, że programiści nie traktują poważnie skryptów powłoki tak bardzo, jak innych języków skryptowych, takich jak Python i Ruby.
codeforester
1
Prawda, że. Wkurza mnie to, że wszyscy myślą, że wszystkie zmienne powinny być wielkimi literami w skryptach powłoki :)
Roger Dahl
2

Po sugestiach SierraX i Petera na temat manipulacji tekstem nawiasy klamrowe {}służą do przekazywania zmiennej do polecenia, na przykład:

Załóżmy, że masz plik sposi.txt zawierający pierwszy wiersz znanej włoskiej powieści:

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

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

Teraz utwórz dwie zmienne:

# 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"

Teraz zastąp zmienną słowo słowem new_word w pliku sposi.txt

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

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

Słowo „ramo” zostało zastąpione.

Fabio Natalini
źródło
1
Działa to równie dobrze bez nawiasów klamrowych wokół zmiennych.
Armali
Możesz naprawić weel-known novelbit. Mimo to głosowano.
gsl