$ VAR vs $ {VAR} dan mengutip atau tidak mengutip

139

Saya bisa menulis

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

hasil akhirnya bagi saya semua tampak hampir sama. Mengapa saya harus menulis yang satu atau yang lain? apakah semua ini tidak portabel / POSIX?

xenoterracide
sumber

Jawaban:

99

VAR=$VAR1adalah versi sederhana dari VAR=${VAR1}. Ada hal-hal yang kedua dapat lakukan yang pertama tidak bisa, misalnya referensi indeks array (tidak portabel) atau menghapus substring (POSIX-portable). Lihat bagian Lebih lanjut tentang variabel pada Panduan Bash untuk Pemula dan Ekspansi Parameter dalam spesifikasi POSIX.

Menggunakan tanda kutip di sekitar variabel sebagai rm -- "$VAR1"atau rm -- "${VAR}"adalah ide yang baik. Ini menjadikan isi variabel sebagai unit atom. Jika nilai variabel berisi kosong (well, karakter dalam $IFSvariabel khusus, kosong secara default) atau karakter globbing dan Anda tidak mengutipnya, maka setiap kata dipertimbangkan untuk pembuatan nama file (globbing) yang perluasannya membuat banyak argumen untuk apa pun yang Anda sedang dilakukan.

$ 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

Pada portabilitas: Menurut POSIX.1-2008 bagian 2.6.2 , kurung kurawal adalah opsional.

Shawn J. Goff
sumber
@shawn memperbarui pertanyaan saya karena saya juga ingin tahu tentang portabilitas
xenoterracide
@shawn: Saya ragu contoh Anda valid. Apakah Anda memiliki contoh nyata dari shell di mana var1=$varekspansi memberikan kesalahan?
alex
@alex: Terima kasih. Saya pikir saya telah menguji itu di baris perintah, tetapi saya salah melakukannya. Saya mengubah contoh.
Shawn J. Goff
Dengan contoh yang diperbarui lebih baik untuk diingat bahwa Anda biasanya menginginkan versi yang dikutip, karena contohnya lebih merupakan kasus sudut.
alex
9
@Shawn: Kutipan tidak diperlukan dalam penugasan. Mereka diperlukan dalam sebagian besar kegunaan lain, termasuk export VAR=$VAR1. Sedangkan untuk kawat gigi, mereka adalah opsional (periksa paragraf keempat dari bagian yang Anda kutip; ini adalah kasus di semua shell pra-POSIX dan POSIX).
Gilles
60

${VAR}dan $VARpersis sama. Untuk ekspansi variabel biasa, satu-satunya alasan untuk digunakan ${VAR}adalah ketika penguraian sebaliknya akan mengambil terlalu banyak karakter ke dalam nama variabel, seperti dalam ${VAR1}_$VAR2(yang tanpa kawat gigi akan setara dengan ${VAR1_}$VAR2). Ekspansi yang paling menghiasi ( ${VAR:=default}, ${VAR#prefix}, ...) membutuhkan kawat gigi.

Dalam penugasan variabel, pemisahan bidang (mis. Pemisahan pada spasi putih pada nilai) dan perluasan pathname (yaitu penggumpalan) dimatikan, jadi VAR=$VAR1persis sama dengan VAR="$VAR1", di semua shell POSIX dan di semua sh pre-POSIX yang pernah saya dengar . (POSIX ref: perintah sederhana ). Untuk alasan yang sama, VAR=*andal disetel VARke string literal *; tentu saja VAR=a bdiatur VARke akarena kata bitu terpisah di tempat pertama. Secara umum, tanda kutip ganda tidak diperlukan di mana sintaks shell mengharapkan satu kata, misalnya dalamcase … in (tetapi tidak dalam pola), tetapi bahkan di sana Anda harus berhati-hati: misalnya POSIX menentukan bahwatarget pengalihan ( >$filename) tidak memerlukan kuotasi dalam skrip, tetapi beberapa shell termasuk bash memang membutuhkan kuotasi ganda bahkan dalam skrip. Lihat Kapan perlu kutip ganda? untuk analisis yang lebih menyeluruh.

Anda memang membutuhkan tanda kutip ganda dalam kasus lain, khususnya dalam export VAR="${VAR1}"(yang dapat dituliskan secara setara export "VAR=${VAR1}") dalam banyak shell (POSIX membiarkan case ini terbuka). Kesamaan kasus ini dengan penugasan sederhana, dan sifat daftar kasus yang tersebar di mana Anda tidak memerlukan tanda kutip ganda, itulah sebabnya saya sarankan hanya menggunakan tanda kutip ganda kecuali jika Anda ingin membagi dan menggumpal.

Gilles
sumber
2
Sebagai aturan umum, saya akan selalu mengutip ekspansi variabel bahkan ketika saya tahu nilainya tidak akan mengandung IFSkarakter apa pun karena saya ingin terbiasa. Satu pengecualian adalah saya tidak mengutip nilai ketika melakukan penugasan variabel (kecuali diperlukan, seperti ketika nilai mengandung spasi). Ini membuat penyorotan sintaksis editor lebih berguna ketika ada substitusi perintah seperti FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Alih-alih mewarnai semua warna "string", saya mendapatkan highlight sintaks dari kode bersarang. Ini juga sebabnya saya menghindari backticks.
Richard Hansen
Meskipun >$fileOK di skrip POSIX, itu tidak di bash bahkan ketika non-interaktif (kecuali kepatuhan POSIX diberlakukan dengan $POSIXLY_CORRECTatau --posix...).
Stéphane Chazelas
Meskipun benar bahwa kutipan tidak diperlukan VAR=$VAR1, kadang-kadang saya terkejut olehnya local VAR=$VAR1, yang saya ingat bekerja secara berbeda dalam beberapa hal, setidaknya dalam beberapa hal. Tapi atm, saya tidak bisa mereproduksi perbedaan.
dubiousjim
Oke, temukan masalah yang saya ingat . Itu hanya muncul di beberapa shell.
dubiousjim
@dubiousjim local VAR=$VAR1seperti export VAR=$VAR1, itu tergantung pada shell.
Gilles
8

Kutipan

Pertimbangkan bahwa kutipan ganda digunakan untuk ekspansi variabel, dan kutipan tunggal digunakan untuk penawaran kuat, yaitu, ekspansi sans.

Ekspansi:

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

Keluaran:

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

Mungkin bermanfaat untuk menyebutkan bahwa Anda harus menggunakan kutipan sedapat mungkin karena beberapa alasan, di antaranya adalah yang dianggap praktik terbaik, dan untuk keterbacaan. Juga karena Bash kadang-kadang unik dan sering untuk cara yang tampaknya tidak logis atau tidak masuk akal / tidak terduga, dan kutipan mengubah ekspektasi implisit ke eksplisit, yang mengurangi permukaan kesalahan (atau potensi-untuknya).

Dan meskipun sepenuhnya legal untuk tidak mengutip, dan akan berfungsi dalam banyak kasus, fungsionalitas itu disediakan untuk kenyamanan dan mungkin lebih mudah dibawa-bawa. praktik formal sepenuhnya dijamin untuk mencerminkan niat dan harapan adalah mengutip.

Pengganti

Sekarang perhatikan juga bahwa konstruk "${somevar}"tersebut digunakan untuk operasi substitusi. Beberapa kasus penggunaan, seperti penggantian, dan array.

Penggantian (pengupasan):

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

Penggantian (replacement):

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

Array:

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

Semua ini nyaris tidak menggores permukaan "${var}"konstruksi substitusi. Referensi definitif untuk skrip Bash shell adalah referensi online gratis, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

SYANiDE
sumber
1
sangat informatif.
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

akhir saja:

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

layak disebut sebagai contoh yang lebih jelas tentang penggunaan ikal

ninjabber
sumber