różnica między $ {} i $ () w skrypcie powłoki

28
$ echo $(date)
Thu Jul 2 16:33:11 SGT 2015
$ echo ${date}

$ name=foo
$ echo $(name)
ksh: name:  not found

$ echo ${name}
foo

Wygląda na to, że $ {zmienna} jest taka sama jak $ zmienna. podczas gdy $ () ma wykonać polecenie. Po co więc korzystać z $ {}?

Nowicjusz
źródło
1
Powiązane:  nie znaczy, co myślisz, że robi…${ variable_name }
G-Man mówi „Przywróć Monikę”

Odpowiedzi:

39

$(command)to „podstawienie polecenia”. Jak się wydaje, rozumiesz, uruchamia on command, przechwytuje dane wyjściowe i wstawia je do wiersza poleceń zawierającego $(…); na przykład,

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

${parameter}to „podstawienie parametru”. Wiele informacji można znaleźć na stronie man powłoki, bash (1) , pod nagłówkiem „ Parameter Expansion ”:

${parameter}
    Wartość parametru jest podstawiona. Nawiasy klamrowe są wymagane, gdy parametr jest parametrem pozycyjnym z więcej niż jedną cyfrą lub gdy po parametrze występuje znak, którego nie należy interpretować jako części jego nazwy.

Aby zapoznać się z parametrami pozycyjnymi, patrz „ Parametry pozycyjne ” poniżej. W najczęstszym użyciu, jak pokazano w innych odpowiedziach, parameterjest nazwa zmiennej. ${…}Forma, jak podano na końcu ustępie powyżej, pozwala uzyskać wartość zmiennej (tj ), a następnie ją natychmiast z literą, cyfrą lub podkreślenia:$variable_name

$ zwierzę = kot
$ echo $ zwierzęta
                                # Brak takiej zmiennej jak „zwierzęta”.
$ echo $ {animal} s
koty
$ echo $ animal_food
                                # Brak takiej zmiennej jak „animal_food”.
$ echo $ {animal} _food
karma dla kotów

Możesz to również zrobić za pomocą cytatów:

$ echo "$ animal" s
koty

Lub, jako ćwiczenie opcji, możesz użyć drugiej zmiennej:

$ liczba mnoga = s
$ echo $ animal $ liczba mnoga
koty

Ale to tylko krok 1. Następny akapit na stronie podręcznika jest interesujący, choć trochę tajemniczy:

Jeśli pierwszym znakiem parametru jest wykrzyknik ( !), wprowadzany jest poziom zmiennej pośredniej. Bash używa wartości zmiennej utworzonej z reszty parametru jako nazwy zmiennej; zmienna ta jest następnie rozszerzana i ta wartość jest używana w pozostałej części podstawienia, a nie w wartości samego parametru . Jest to znane jako ekspansja pośrednia .     (Wyjątki)    Wykrzyknik musi znajdować się zaraz za lewym nawiasiem, aby wprowadzić pośrednie.

Nie jestem pewien, jak mogę to wyjaśnić inaczej niż na przykładzie:

$ zwierzę = kot
$ echo $ zwierzę
kot
$ cat = tabby
$ echo $ cat
mora
$ echo $ {! animal}
tabby                            # Jeśli $ animal to „cat” , to $ {! animal} to $ cat , tzn. „tabby”

Nazwijmy więc ten krok 1½. Istnieje wiele ciekawych rzeczy, które możesz zrobić jako krok 2:

$ zwierzę = kot
$ echo $ {# animal}
3                                # Długość łańcucha
$ echo $ {animal / at / ow}
krowa                              # Zmiana

Nie możesz zrobić żadnej z tych rzeczy bez {}aparatów ortodontycznych.

Parametry pozycyjne

Rozważ ten sztuczny przykład:

$ cat myecho.sh
echo 1 $ 2 $ 3 $ 4 $ 5 $ 6 $ 7 $ 8 $ 9 $ 10 $ 11 $ 12 $ 13 $ 14 $ 15 $
$ ./myecho.sh Hej diddle diddle, Kot i skrzypce, Krowa przeskoczyła księżyc.
Hej diddle diddle, Kot i skrzypce, The Hey0 Hey1 Hey2 Hey3 Hey4 Hey5

ponieważ powłoka nie rozumie $10, $11itp Traktuje $10jakby to było ${1}0. Ale to nie rozumie ${10}, ${11}itp, jak wspomniano na stronie man ( „A parametru pozycyjnego z więcej niż jedną cyfrę”).

Ale tak naprawdę nie pisz takich skryptów; istnieją lepsze sposoby radzenia sobie z długimi listami argumentów.

Powyższe (wraz z wieloma innymi formami konstrukcji) są omówione bardziej szczegółowo na stronie podręcznika powłoki, bash (1) .${parameter…something_else}

Uwaga na cytaty

Pamiętaj, że zawsze powinieneś cytować zmienne powłoki, chyba że masz dobry powód, aby tego nie robić , i jesteś pewien, że wiesz, co robisz. Natomiast nawiasy klamrowe mogą być ważne, ale nie są tak ważne jak cytaty.

$ filename = "szkółka rhyme.txt"
$ ls -ld $ {nazwa pliku}
ls: nie można uzyskać dostępu do pokoju dziecinnego: brak takiego pliku lub katalogu
ls: nie można uzyskać dostępu do rhyme.txt: nie ma takiego pliku ani katalogu
$ ls -ld "$ nazwa pliku"
-rwxr-xr-x 1 Noob Noob 5309 2 lipca 11:09 rymowanka.txt

Dotyczy to również parametrów pozycyjnych (tj. Argumentów wiersza poleceń; np. "$1"), A także podstawiania poleceń:

$ ls -ld $ (data „+% B% Y”). txt
ls: nie można uzyskać dostępu do lipca: brak takiego pliku lub katalogu
ls: nie można uzyskać dostępu do 2015.txt: brak takiego pliku lub katalogu
$ ls -ld "$ (data" +% B% Y "). txt"
-rwxr-xr-x 1 Noob Noob 687 2 lipca 11:09 lipca 2015.txt

Zobacz Bash cytaty Niecytowany od podstawienia polecenia na krótki traktat na temat interakcji pomiędzy cytatów i $(... ).

G-Man mówi „Przywróć Monikę”
źródło
dzięki za wspaniały przykład. Czy możesz opracować sposób użycia! w twoim przykładzie. Naprawdę nie rozumiem, jak to działa.
Noob
@Noob: OK, opracowałem użycie !.
G-Man mówi „Przywróć Monikę”
dzięki. Jakoś! Zwierzę faktycznie odnosi się do zmiennej cat zamiast wartości cat. Czy możesz mi również powiedzieć, w jaki sposób / at staje się „c” w echu $ {animal / at / ow} Wyjaśnienie człowieka bash jest jakoś ... nie wiem. ciężko zrozumieć.
Noob
czy możesz rozwinąć tę frazę - „$ {parametr} Wartość parametru jest podstawiona”. zastąpiony czym? jeśli parametrem jest owoc, a jego wartość to jabłko - owoc = jabłko, więc $ {fruit} - jabłko zastępuje się ?? tak naprawdę nie
zastąpię
@Noob: „w ${!animal}rzeczywistości odnosi się do zmiennej $catzamiast wartości cat.” Tak, właśnie o to chodzi. „Jak / at staje się„ c ”w echu $ {animal / at / ow}?” Huh? / at nie staje się „c”; „Kot” staje się „krową”, gdy „at” jest zastąpione przez „ow”.
G-Man mówi „Przywróć Monikę”
7

W twoim przykładzie $ var i $ {var} są identyczne. Jednak nawiasy klamrowe są przydatne, gdy chcesz rozwinąć zmienną w ciągu:

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

    $ 

W ten sposób nawiasy klamrowe umożliwiają zastąpienie zmiennej w celu uzyskania nazwy nowej zmiennej, która ma zostać zastąpiona.

MariusMatutiae
źródło
4

Zwykle widzę to częściej w ciągach. Coś takiego nie będzie działać:

var="a"
echo "$varRAW_STRING"

Ale to:

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

Jak poprawnie powiedziałeś, $()służy do wykonania polecenia:

dir_contents=$(ls)

Możesz także użyć backticksa, ale uważam, że jest $()bardziej uniwersalny. Po pierwsze, backticks nie mogą być (łatwo) zagnieżdżone.

date_directory=`ls `date '+%Y-%m-%d'`` # Makes no sense
date_directory=$(ls $(date '+%Y-%m-%d')) # Much better
bajtowany
źródło
Właściwie backticks może być zagnieżdżona: date_directory=`ls \`date '+%Y-%m-%d'\``. Ale to jest strasznie brzydkie; $(…)jest znacznie jaśniejszy i łatwiejszy w użyciu.
G-Man mówi „Przywróć Monikę”
Ok, w porządku. Jest to technicznie możliwe, ale nie wyobrażam sobie, aby ktokolwiek kiedykolwiek to robił tylko ze względu na czytelność
bajtowano
1
Cóż, `…`był (tylko) składnia podstawiania poleceń lat zanim wynaleziono, więc nie trzeba wyobrazić sobie cokolwiek - ludzie to zrobił. $(…)
G-Man mówi „Przywróć Monikę”
s / ever robi to / robi to już /
bajtował