Odwoływanie się do zmiennych w skrypcie powłoki

10

Jeśli w skrypcie powłoki zdefiniuję zmienną taką jak FOO=25, czy istnieje różnica między odwoływaniem się do niej jako $FOO$i ${FOO}$?

Karnivaurus
źródło
2
Nie wolno używać $w końcu! Będzie traktowany jako normalny znak, a nie jako część nazwy zmiennej.
Bajt Dowódca

Odpowiedzi:

16

W większości przypadków oba $vari ${var}są takie same. (Pamiętaj, że nie wolno używać $na końcu!)

Jednym z przykładów, w których potrzebujesz nawiasów klamrowych, jest umieszczenie zmiennej w ciągłym łańcuchu.

Przykład:

var=hel
echo ${var}lo

wyjdzie hello.

Aizuddin Zali
źródło
11

Aby odpowiedzieć na twoje główne pytanie, ${foo}nazywa się to „rozszerzaniem parametrów” . Mówiąc ściślej , $sam rozpoczyna ekspansję parametrów, { }są one właściwie opcjonalne zgodnie ze specyfikacją POSIX , ale mogą być przydatne do wskazania końca nazwy zmiennej:

$ foo="bar"
$ echo $fooBaz   ## fails, no variable named $fooBaz exists

$ echo ${foo}Baz ## works, $foo is expanded and the string Baz is appended
barBaz

Zasadniczo $fooi ${foo}są identyczne. Oprócz przypadków takich jak powyższe lub podczas manipulacji ciągami są one całkowicie równoważne.

Jednak tak naprawdę nie powinieneś używać żadnego z nich. Ogólna zasada jest taka, że ​​z nielicznymi wyjątkami należy zawsze używać "$foo"lub "${foo}"nigdy, $foolub nigdy ${foo}. Zawsze powinieneś cytować swoje zmienne, aby uniknąć wywoływania operatora split + glob (więcej na ten temat później). Na pewno nie chcesz $foo$. Finał nie $ma znaczenia:

$ foo="bar"
$ echo "$foo$"
bar$

Tak więc, chociaż zmienne nienotowane są czasem OK:

$ echo $foo
bar

Zazwyczaj nie są i tak naprawdę należy ich unikać:

$ if [ -n $foo ]; then echo empty; else echo "not empty"; fi ## fails
empty

$ if [ -n "$foo" ]; then echo empty; else echo "not empty"; fi ## works
not empty

Zauważ, że nawiasy kwadratowe również tutaj nie pomagają:

$ if [ -n ${foo} ]; then echo empty; else echo "not empty"; fi  ## fails
empty

$ if [ -n "${foo}" ]; then echo empty; else echo "not empty"; fi ## works
not empty

Kiedy używasz $foolub ${foo}, powłoka podzieli wartość zapisaną w zmiennej na białe znaki (można to zmienić, ustawiając IFSzmienną na coś innego) na listę, a następnie każdy element listy jest traktowany jako wzorzec globalny i rozwijany do wszystkie pasujące pliki lub katalogi. Jest to znane jako operator split + glob . Aby to zilustrować, rozważ katalog z dwoma plikami:

$ ls -l
-rw-r--r-- 1 terdon terdon 0 Oct  9 18:16 file1
-rw-r--r-- 1 terdon terdon 0 Oct  9 18:16 file2

Teraz ustawmy zmienną na foo *:

$ foo="foo *"

Co się stanie, jeśli spróbujemy sprawdzić, czy istnieje plik o tej nazwie?

$ if [ -e $foo ]; then echo "file exists"; else echo "no such file"; fi
file exists

Zmienna została podzielona na fooi *, ponieważ ponieważ *jest to symbol wieloznaczny pasujący do dowolnego ciągu, powłoka informuje, że plik o nazwie foo *exxists. Jeśli jednak podamy to poprawnie, nie stanie się tak:

$ if [ -e "$foo" ]; then echo "file exists"; else echo "no such file"; fi
no such file

To był trywialny przykład ilustrujący tę kwestię. Wyobraź sobie, że rmzamiast tego użyłem echo.

Pierwsza zasada: zawsze podawaj swoje zmienne. Możesz użyć albo, "$foo"albo "${foo}"zacytować to tak czy inaczej. Aby uzyskać więcej informacji na temat bezpiecznego używania zmiennych, spójrz na te posty:

terdon
źródło
Nie chciałem edytować inaczej doskonałą odpowiedź, ale nie powinno $ var="foo *"być $ foo="foo *"? varNigdzie nie używasz .
Joe
@Joe och, dobry żal! Tak, oczywiście, że powinien, dziękuję za zwrócenie na to uwagi. Nawiasem mówiąc, możesz naprawić takie błędy, sugerując zmianę. Takie rzeczy są bardzo zachęcane, właśnie po to, aby uniknąć takich głupich błędów, jak ten.
terdon