$ VAR vs $ {VAR} i cytować lub nie cytować

139

Umiem pisać

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

wynik końcowy wydaje mi się taki sam. Dlaczego mam pisać jedno lub drugie? czy któreś z nich nie jest przenośne / POSIX?

ksenoterracid
źródło

Odpowiedzi:

99

VAR=$VAR1jest uproszczoną wersją VAR=${VAR1}. Są rzeczy, które może zrobić drugi, czego pierwszy nie może, na przykład odwołać się do indeksu tablicy (nieprzenośny) lub usunąć podciąg (POSIX-przenośny). Zobacz sekcję Więcej na temat zmiennych w Bash Guide dla początkujących i rozszerzaniu parametrów w specyfikacji POSIX.

Używanie cudzysłowów wokół zmiennej jak w rm -- "$VAR1"lub rm -- "${VAR}"jest dobrym pomysłem. To sprawia, że ​​zawartość zmiennej jest jednostką atomową. Jeśli wartość zmiennej zawiera spacje (cóż, znaki w $IFSzmiennej specjalnej, domyślnie spacje) lub znaki globowania, a nie zacytujesz, to każde słowo jest brane pod uwagę przy generowaniu nazw plików (globbing), których rozwinięcie daje tyle argumentów, co ty robię.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

Przenośność: zgodnie z POSIX.1-2008 sekcja 2.6.2 nawiasy klamrowe są opcjonalne.

Shawn J. Goff
źródło
@shawn zaktualizował moje pytanie, ponieważ jestem również ciekawy przenośności
xenoterracide
@shawn: Wątpię, czy Twój przykład jest prawidłowy. Czy masz jakiś prawdziwy przykład powłoki, w której var1=$varrozszerzenie powoduje błąd?
alex
@alex: Dzięki. Myślałem, że przetestowałem to w linii poleceń, ale zrobiłem to źle. Zmieniłem przykład.
Shawn J. Goff,
W zaktualizowanym przykładzie lepiej jest pamiętać, że ogólnie powinieneś chcieć cytowanej wersji, ponieważ przykład jest raczej narożny.
alex
9
@Shawn: Cytaty nie są konieczne w zadaniu. Są niezbędne w większości innych zastosowań, w tym export VAR=$VAR1. Jeśli chodzi o nawiasy klamrowe, są one opcjonalne (sprawdź czwarty akapit cytowanej sekcji; dotyczy to wszystkich powłok wcześniejszych niż POSIX i POSIX).
Gilles
60

${VAR}i $VARsą dokładnie równoważne. W przypadku zwykłego rozszerzania zmiennych jedynym powodem jest użycie, ${VAR}gdy parsowanie w przeciwnym razie przyciągałoby zbyt wiele znaków do nazwy zmiennej, jak w ${VAR1}_$VAR2(co bez nawiasów byłoby równoważne ${VAR1_}$VAR2). Większość zdobione ekspansje ( ${VAR:=default}, ${VAR#prefix}...) wymagają szelki.

Przy przypisywaniu zmiennych dzielenie pól (tj. Dzielenie na białą wartość w wartości) i rozwijanie nazw ścieżek (tj. Globowanie) są wyłączone, więc VAR=$VAR1jest dokładnie równoważne z VAR="$VAR1", we wszystkich powłokach POSIX i we wszystkich wersjach wcześniejszych niż POSIX, o których słyszałem . (POSIX ref: proste polecenia ). Z tego samego powodu VAR=*niezawodnie ustawia VARciąg literalny *; oczywiście VAR=a bustawia się VARna, aponieważ bto przede wszystkim osobne słowo. Ogólnie rzecz biorąc, podwójne cudzysłowy są niepotrzebne, gdy składnia powłoki oczekuje pojedynczego słowa, na przykład wcase … in (ale nie we wzorcu), ale nawet tam należy zachować ostrożność: na przykład POSIX określa, żecele przekierowania ( >$filename) nie wymagają cytowania w skryptach, ale kilka powłok, w tym bash, wymaga podwójnych cytatów nawet w skryptach. Zobacz Kiedy konieczne jest podwójne cytowanie? dla dokładniejszej analizy.

W innych przypadkach potrzebujesz podwójnych cudzysłowów, w szczególności w export VAR="${VAR1}"(które można zapisać export "VAR=${VAR1}"w równoważny sposób ) w wielu powłokach (POSIX pozostawia tę skrzynkę otwartą). Podobieństwo tego przypadku do prostych zadań i rozproszony charakter listy przypadków, w których nie potrzebujesz podwójnych cudzysłowów, dlatego właśnie zalecam stosowanie podwójnych cudzysłowów, chyba że chcesz podzielić i zsumować.

Gilles
źródło
2
Zasadniczo zawsze będę cytował rozszerzenia, nawet gdy wiem, że wartość nie będzie zawierać żadnych IFSznaków, ponieważ chcę mieć nawyk. Jedynym wyjątkiem jest to, że nie cytuję wartości podczas przypisywania zmiennej (chyba że jest to wymagane, na przykład gdy wartość zawiera spację). To sprawia, że ​​podświetlanie składni edytora jest bardziej przydatne, gdy występują zastępowania poleceń, takie jak FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Zamiast kolorować wszystko kolorem „ciągowym”, podświetlam składnię zagnieżdżonego kodu. Dlatego też unikam uderzeń zwrotnych.
Richard Hansen
Chociaż >$filejest dobrze w skryptach POSIX, nie jest bash, nawet gdy nie jest interaktywny (chyba że wymuszona jest zgodność z POSIX $POSIXLY_CORRECTlub --posix...).
Stéphane Chazelas
Chociaż prawdą jest, że cytaty nie są potrzebne VAR=$VAR1, czasami byłem zaskoczony local VAR=$VAR1, co, jak pamiętam, działało inaczej pod pewnymi względami, przynajmniej w niektórych powłokach. Ale atm, nie mogę odtworzyć rozbieżności.
dubiousjim
Ok, znalazłem problem, który pamiętam . Pokazuje się tylko w niektórych powłokach.
dubiousjim
@ dubiousjim local VAR=$VAR1jest jak export VAR=$VAR1, to zależy od powłoki.
Gilles
8

Cytat

Weź pod uwagę, że podwójny cudzysłów służy do zmiennej interpretacji, a pojedynczy cudzysłów służy do silnego cytowania, tj. Bez rozszerzenia.

Ekspansja:

this='foo'
that='bar'
these="$this"
those='$that'

Wynik:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Warto wspomnieć, że w miarę możliwości należy stosować cytat z kilku powodów, między innymi z tego, że jest to uważane za najlepszą praktykę i ze względu na czytelność. Również dlatego, że Bash jest czasem dziwaczny i często z pozornie nielogicznych lub nieuzasadnionych / nieoczekiwanych sposobów, a cytowanie zmienia jawne oczekiwania, co zmniejsza jawną powierzchnię błędu (lub potencjalną możliwość wystąpienia).

I choć jest to całkowicie legalne nie cytować, i będzie działać w większości przypadków, że funkcjonalność jest dla wygody i jest prawdopodobnie mniej przenośne. cytowana jest w pełni formalna praktyka gwarantująca odzwierciedlenie zamiarów i oczekiwań.

Podstawienie

Teraz rozważ również, że konstrukcja "${somevar}"jest używana do operacji podstawienia. Kilka przypadków użycia, takich jak zamiana i tablice.

Wymiana (stripping):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Wymiana (wymiana):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Tablice:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Wszystko to ledwo rysuje powierzchnię "${var}"konstruktu podstawienia. Ostatecznym odniesieniem dla skryptów powłoki Bash jest bezpłatne odniesienie online, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

SYANiDE
źródło
1
bardzo informujące.
orion elenzil
0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

koniec wtedy:

env=$1
    if [ ! -f /dirname/${env}hostname ]

Warto wspomnieć o jaśniejszym przykładzie używania loków

ninjabber
źródło