Jika Anda telah mengikuti unix.stackexchange.com untuk sementara waktu, semoga Anda tahu sekarang bahwa meninggalkan variabel yang tidak dikutip dalam konteks daftar (seperti pada echo $var
) di shell Bourne / POSIX (zsh menjadi pengecualian) memiliki arti yang sangat istimewa dan tidak boleh dilakukan kecuali Anda memiliki alasan yang sangat bagus untuk itu.
Ini dibahas panjang lebar di sejumlah Q & A disini (Contoh: ? Mengapa saya shell script yang tersedak spasi atau karakter khusus lainnya , Kapan double-mengutip diperlukan? , Perluasan variabel shell dan efek gumpal dan perpecahan di atasnya , Dikutip vs ekspansi string yang tidak dikutip)
Itulah yang terjadi sejak rilis pertama dari shell Bourne pada akhir 70-an dan belum diubah oleh shell Korn (salah satu penyesalan terbesar David Korn (pertanyaan # 7) ) atau bash
yang sebagian besar menyalin shell Korn, dan itulah bagaimana yang telah ditentukan oleh POSIX / Unix.
Sekarang, kami masih melihat sejumlah jawaban di sini dan bahkan kadang-kadang dirilis kode shell di mana variabel tidak dikutip. Anda akan berpikir orang akan belajar sekarang.
Dalam pengalaman saya, terutama ada 3 jenis orang yang tidak mengutip variabel mereka:
pemula. Mereka dapat dimaafkan karena memang itu sintaks yang benar-benar tidak intuitif. Dan itu adalah peran kami di situs ini untuk mendidik mereka.
orang yang pelupa.
orang-orang yang tidak yakin bahkan setelah berulang kali memalu, yang berpikir bahwa pastinya penulis shell Bourne tidak bermaksud kita untuk mengutip semua variabel kita .
Mungkin kita bisa meyakinkan mereka jika kita mengekspos risiko yang terkait dengan perilaku semacam ini.
Apa hal terburuk yang mungkin terjadi jika Anda lupa mengutip variabel Anda. Apakah itu benar-benar yang buruk?
Kerentanan macam apa yang kita bicarakan di sini?
Dalam konteks apa itu bisa menjadi masalah?
Jawaban:
Pembukaan
Pertama, saya katakan itu bukan cara yang tepat untuk mengatasi masalah tersebut. Ini seperti mengatakan " Anda tidak harus membunuh orang karena kalau tidak Anda akan masuk penjara ".
Demikian pula, Anda tidak mengutip variabel Anda karena jika tidak Anda memperkenalkan kerentanan keamanan. Anda mengutip variabel Anda karena itu salah untuk tidak (tetapi jika takut penjara dapat membantu, mengapa tidak).
Ringkasan kecil untuk mereka yang baru saja naik kereta.
Dalam kebanyakan shell, membiarkan ekspansi variabel tidak dikutip (meskipun itu (dan sisa jawaban ini) juga berlaku untuk substitusi perintah (
`...`
atau$(...)
) dan ekspansi aritmatika ($((...))
atau$[...]
)) memiliki arti yang sangat istimewa. Cara terbaik untuk mendeskripsikannya adalah seperti memanggil semacam operator + pembelahan implisit¹ .dalam bahasa lain akan dituliskan sesuatu seperti:
$var
pertama-tama dipecah menjadi daftar kata sesuai dengan aturan kompleks yang melibatkan$IFS
parameter khusus (bagian terbagi ) dan kemudian setiap kata yang dihasilkan dari pemisahan itu dianggap sebagai pola yang diperluas ke daftar file yang cocok dengan itu (bagian gumpalan ) .Sebagai contoh, jika
$var
berisi*.txt,/var/*.xml
dan$IFS
mengandung,
,cmd
akan dipanggil dengan sejumlah argumen, yang pertama adalahcmd
yang berikutnya adalahtxt
file dalam direktori saat ini danxml
file dalam/var
.Jika Anda ingin menelepon
cmd
hanya dengan dua argumen literalcmd
dan*.txt,/var/*.xml
, Anda akan menulis:yang akan menggunakan bahasa lain yang lebih Anda kenal:
Apa yang kita maksud dengan kerentanan di shell ?
Bagaimanapun, sudah diketahui sejak awal waktu bahwa skrip shell tidak boleh digunakan dalam konteks sensitif-keamanan. Tentunya, OK, membiarkan variabel tidak dicantumkan adalah bug, tetapi itu tidak banyak merugikan, bukan?
Yah, terlepas dari kenyataan bahwa siapa pun akan memberi tahu Anda bahwa skrip shell tidak boleh digunakan untuk CGI web, atau untungnya sebagian besar sistem tidak mengizinkan skrip shell setuid / setgid saat ini, satu hal yang shellshock (bug bash yang dapat dieksploitasi dari jarak jauh yang membuat berita utama pada bulan September 2014) mengungkapkan bahwa shell masih digunakan secara luas di mana mereka seharusnya tidak boleh: di CGI, dalam skrip kait klien DHCP, dalam perintah sudoers, dipanggil oleh (jika bukan sebagai ) perintah setuid ...
Terkadang tanpa sadar. Sebagai contoh
system('cmd $PATH_INFO')
dalam skripphp
/perl
/python
CGI memang memanggil shell untuk menafsirkan baris perintah itu (belum lagi fakta bahwacmd
itu sendiri mungkin merupakan skrip shell dan penulisnya mungkin tidak pernah berharap akan dipanggil dari CGI).Anda memiliki kerentanan ketika ada jalan untuk peningkatan hak istimewa, yaitu ketika seseorang (sebut saja penyerangnya ) dapat melakukan sesuatu yang tidak seharusnya.
Selalu itu berarti penyerang menyediakan data, bahwa data sedang diproses oleh pengguna / proses istimewa yang secara tidak sengaja melakukan sesuatu yang seharusnya tidak dilakukan, dalam sebagian besar kasus karena bug.
Pada dasarnya, Anda memiliki masalah ketika kode kereta Anda memproses data di bawah kendali penyerang .
Sekarang, tidak selalu jelas dari mana data itu berasal, dan seringkali sulit untuk mengetahui apakah kode Anda akan pernah memproses data yang tidak dipercaya.
Sejauh menyangkut variabel, Dalam kasus skrip CGI, cukup jelas, datanya adalah parameter GET / POST CGI dan hal-hal seperti cookie, path, host ... parameter.
Untuk skrip setuid (berjalan sebagai satu pengguna ketika dipanggil oleh yang lain), itu argumen atau variabel lingkungan.
Vektor lain yang sangat umum adalah nama file. Jika Anda mendapatkan daftar file dari direktori, ada kemungkinan file telah ditanam di sana oleh penyerang .
Dalam hal itu, bahkan pada prompt shell interaktif, Anda bisa rentan (saat memproses file di
/tmp
atau~/tmp
misalnya).Bahkan a
~/.bashrc
bisa rentan (misalnya,bash
akan menafsirkannya ketika dipanggilssh
untuk menjalankanForcedCommand
seperti dalamgit
penyebaran server dengan beberapa variabel di bawah kendali klien).Sekarang, skrip mungkin tidak dipanggil secara langsung untuk memproses data yang tidak dipercaya, tetapi skrip tersebut dapat dipanggil oleh perintah lain yang melakukannya. Atau kode yang salah Anda dapat disalin-salin ke skrip yang melakukannya (oleh Anda 3 tahun ke depan atau salah satu rekan Anda) Satu tempat yang sangat kritis adalah jawaban di situs Q&A karena Anda tidak akan pernah tahu di mana salinan kode Anda mungkin berakhir.
Turun ke bisnis; seberapa buruk?
Meninggalkan variabel (atau substitusi perintah) tanpa tanda kutip sejauh ini merupakan sumber kerentanan keamanan nomor satu yang terkait dengan kode shell. Sebagian karena bug itu sering diterjemahkan menjadi kerentanan tetapi juga karena sangat umum untuk melihat variabel yang tidak dikutip.
Sebenarnya, ketika mencari kerentanan dalam kode shell, hal pertama yang harus dilakukan adalah mencari variabel yang tidak dikutip. Mudah dikenali, seringkali kandidat yang baik, umumnya mudah dilacak kembali ke data yang dikendalikan penyerang.
Ada jumlah tak terbatas cara variabel berubah kutip bisa berubah menjadi kerentanan. Saya hanya akan memberikan beberapa tren umum di sini.
Pengungkapan informasi
Sebagian besar orang akan menabrak bug yang terkait dengan variabel yang tidak dikutip karena pembagian bagian (misalnya, sudah umum bagi file untuk memiliki ruang dalam nama mereka saat ini dan ruang dalam nilai default IFS). Banyak orang akan mengabaikan bagian gumpal . Bagian gumpalan setidaknya sama berbahayanya dengan bagian yang terbelah .
Gumpalan yang dilakukan atas input eksternal yang tidak disanitasi berarti penyerang dapat membuat Anda membaca konten direktori mana pun.
Di:
jika
$unsanitised_external_input
mengandung/*
, itu berarti penyerang dapat melihat konten/
. Bukan masalah besar. Ini menjadi lebih menarik meskipun dengan/home/*
yang memberi Anda daftar nama pengguna pada mesin/tmp/*
,,/home/*/.forward
untuk petunjuk tentang praktik berbahaya lainnya,/etc/rc*/*
untuk layanan yang diaktifkan ... Tidak perlu menyebutkan namanya secara individual. Nilai/* /*/* /*/*/*...
hanya akan daftar seluruh sistem file.Penolakan kerentanan layanan.
Mengambil kasus sebelumnya agak terlalu jauh dan kami punya DoS.
Sebenarnya, setiap variabel yang tidak dikutip dalam konteks daftar dengan input yang tidak bersih setidaknya adalah kerentanan DoS.
Bahkan penulis shell ahli biasanya lupa mengutip hal-hal seperti:
:
adalah perintah no-op. Apa yang mungkin salah?Yang dimaksudkan untuk menetapkan
$1
untuk$QUERYSTRING
jika$QUERYSTRING
itu tidak diset. Itu cara cepat untuk membuat skrip CGI dapat dipanggil dari baris perintah juga.Itu
$QUERYSTRING
masih diperluas dan karena tidak dikutip, operator split + glob dipanggil.Sekarang, ada beberapa gumpalan yang sangat mahal untuk dikembangkan. Yang
/*/*/*/*
satu cukup buruk karena itu berarti daftar direktori hingga 4 level ke bawah. Selain aktivitas disk dan CPU, itu berarti menyimpan puluhan ribu path file (40k di sini di VM server minimal, 10k di antaranya direktori).Sekarang
/*/*/*/*/../../../../*/*/*/*
berarti 40k x 10k dan/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*
cukup untuk membuat mesin terkuat sekalipun.Cobalah sendiri (meskipun bersiaplah untuk mesin Anda crash atau hang):
Tentu saja, jika kodenya adalah:
Kemudian Anda dapat mengisi disk.
Lakukan saja pencarian google di shell cgi atau bash cgi atau ksh cgi , dan Anda akan menemukan beberapa halaman yang menunjukkan cara menulis CGI dalam shell. Perhatikan bagaimana setengah dari mereka yang memproses parameter rentan.
Bahkan David Korn sendiri sangat rentan (lihat penanganan cookie).
hingga kerentanan eksekusi kode arbitrer
Eksekusi kode sewenang-wenang adalah jenis kerentanan terburuk, karena jika penyerang dapat menjalankan perintah apa pun, tidak ada batasan apa yang dapat ia lakukan.
Itu umumnya bagian split yang mengarah ke sana. Pemisahan itu menghasilkan beberapa argumen untuk diteruskan ke perintah ketika hanya satu yang diharapkan. Sementara yang pertama akan digunakan dalam konteks yang diharapkan, yang lain akan berada dalam konteks yang berbeda sehingga berpotensi ditafsirkan secara berbeda. Lebih baik dengan contoh:
Di sini, tujuannya adalah untuk menetapkan konten dari
$external_input
variabel shell kefoo
awk
variabel.Sekarang:
Kata kedua yang dihasilkan dari pemisahan
$external_input
tidak ditugaskanfoo
tetapi dianggap sebagaiawk
kode (di sini yang mengeksekusi perintah arbitrer:)uname
.Itu terutama masalah bagi perintah yang dapat mengeksekusi perintah lain (
awk
,env
,sed
(GNU satu),perl
,find
...) terutama dengan varian GNU (yang menerima pilihan setelah argumen). Kadang-kadang, Anda tidak akan menduga perintah untuk dapat mengeksekusi orang lain sepertiksh
,bash
atauzsh
's[
atauprintf
...Jika kita membuat direktori bernama
x -o yes
, maka tes menjadi positif, karena itu adalah ekspresi kondisional yang sama sekali berbeda yang kami evaluasi.Lebih buruk lagi, jika kita membuat file bernama
x -a a[0$(uname>&2)] -gt 1
, dengan semua implementasi ksh setidaknya (yang mencakupsh
sebagian besar Unices komersial dan beberapa BSD), yang dijalankanuname
karena shell tersebut melakukan evaluasi aritmatika pada operator perbandingan numerik dari[
perintah.Sama dengan
bash
untuk nama file sepertix -a -v a[0$(uname>&2)]
.Tentu saja, jika mereka tidak bisa mendapatkan eksekusi sewenang-wenang, penyerang dapat menerima kerusakan yang lebih kecil (yang dapat membantu untuk mendapatkan eksekusi sewenang-wenang). Perintah apa pun yang dapat menulis file atau mengubah izin, kepemilikan atau memiliki efek utama atau samping dapat dieksploitasi.
Segala macam hal dapat dilakukan dengan nama file.
Dan Anda akhirnya
..
dapat menulis (secara rekursif dengan GNUchmod
).Skrip yang melakukan pemrosesan file secara otomatis di area
/tmp
yang dapat ditulis untuk umum harus ditulis dengan sangat hati-hati.Bagaimana dengan
[ $# -gt 1 ]
Itu sesuatu yang saya temukan menjengkelkan. Beberapa orang turun semua kesulitan bertanya-tanya apakah ekspansi tertentu mungkin bermasalah untuk memutuskan apakah mereka dapat menghilangkan tanda kutip.
Ini seperti mengatakan. Hei, sepertinya
$#
tidak bisa dikenai operator split + glob, mari kita minta shell untuk membagi + glob itu . Atau Hei, mari kita menulis kode yang salah hanya karena bug tidak mungkin terkena .Sekarang seberapa tidak mungkin itu? OKE,
$#
(atau$!
,$?
atau substitusi aritmatika apa pun) hanya boleh berisi angka (atau-
untuk sebagian) sehingga bagian gumpalan keluar. Untuk bagian yang terbagi untuk melakukan sesuatu, yang kita butuhkan hanyalah$IFS
memuat angka (atau-
).Dengan beberapa cangkang,
$IFS
mungkin diwarisi dari lingkungan, tetapi jika lingkungan tidak aman, permainan akan berakhir.Sekarang jika Anda menulis fungsi seperti:
Artinya adalah bahwa perilaku fungsi Anda tergantung pada konteksnya. Atau dengan kata lain,
$IFS
menjadi salah satu masukan untuk itu. Sebenarnya, ketika Anda menulis dokumentasi API untuk fungsi Anda, itu harus seperti:Dan kode yang memanggil fungsi Anda perlu memastikan
$IFS
tidak mengandung digit. Semua itu karena Anda tidak ingin mengetik 2 karakter kutipan ganda itu.Sekarang, agar
[ $# -eq 2 ]
bug itu menjadi kerentanan, Anda perlu entah bagaimana agar nilainya$IFS
menjadi di bawah kendali penyerang . Dapat dibayangkan, itu tidak akan terjadi kecuali penyerang berhasil mengeksploitasi bug lain.Tapi itu tidak pernah terdengar. Kasus yang umum adalah ketika orang lupa membersihkan data sebelum menggunakannya dalam ekspresi aritmatika. Kita telah melihat di atas bahwa itu dapat memungkinkan eksekusi kode arbitrer di beberapa shell, tetapi di semua itu, memungkinkan penyerang untuk memberikan variabel nilai integer.
Misalnya:
Dan dengan
$1
nilai(IFS=-1234567890)
, evaluasi aritmatika tersebut memiliki efek samping dari pengaturan IFS dan[
perintah berikutnya gagal yang berarti pemeriksaan untuk terlalu banyak argumen dilewati.Bagaimana dengan ketika operator split + glob tidak dipanggil?
Ada kasus lain di mana tanda kutip diperlukan di sekitar variabel dan ekspansi lainnya: ketika itu digunakan sebagai pola.
jangan menguji apakah
$a
dan$b
sama (kecuali denganzsh
) tetapi jika$a
cocok dengan pola di$b
. Dan Anda perlu untuk mengutip$b
jika Anda ingin membandingkan sebagai string (hal yang sama di"${a#$b}"
atau"${a%$b}"
atau"${a##*$b*}"
di mana$b
harus dikutip jika tidak diambil sebagai pola).Apa itu artinya bahwa
[[ $a = $b ]]
mungkin kembali benar dalam kasus di mana$a
berbeda dari$b
(misalnya ketika$a
adalahanything
dan$b
adalah*
) atau dapat kembali palsu ketika mereka identik (misalnya ketika kedua$a
dan$b
yang[a]
).Bisakah itu membuat kerentanan keamanan? Ya, seperti bug apa pun. Di sini, penyerang dapat mengubah alur kode logis skrip Anda dan / atau mematahkan asumsi yang dibuat skrip Anda. Misalnya, dengan kode seperti:
Penyerang dapat melewati cek dengan melewati
'[a]' '[a]'
.Sekarang, jika tidak ada pencocokan pola atau operator glob + split , apa bahaya meninggalkan variabel yang tidak dikutip?
Saya harus mengakui bahwa saya memang menulis:
Di sana, mengutip tidak membahayakan tetapi tidak sepenuhnya diperlukan.
Namun, satu efek samping dari menghilangkan kuotasi dalam kasus-kasus tersebut (misalnya dalam jawaban Q&A) adalah bahwa ia dapat mengirim pesan yang salah kepada para pemula:
bahwa boleh saja tidak mengutip variabel.Sebagai contoh, mereka mungkin mulai berpikir bahwa jika
a=$b
OK, makaakan baik (yang tidak dalam banyak shell seperti dalam argumen keexport a=$b
export
perintah sehingga dalam konteks daftar) atau.env a=$b
Bagaimana dengan
zsh
?zsh
memang memperbaiki sebagian besar canggung desain. Dalamzsh
(setidaknya ketika tidak dalam mode emulasi sh / ksh), jika Anda ingin membelah , atau menggumpal , atau mencocokkan pola , Anda harus memintanya secara eksplisit:$=var
untuk membelah, dan$~var
untuk menggumpal atau agar isi dari variabel diperlakukan sebagai sebuah pola.Namun, pemisahan (tetapi tidak globbing) masih dilakukan secara implisit pada substitusi perintah yang tidak dikutip (seperti dalam
echo $(cmd)
).Juga, efek samping yang kadang-kadang tidak diinginkan dari tidak mengutip variabel adalah penghapusan kosong . The
zsh
perilaku ini mirip dengan apa yang dapat Anda capai dalam kerang lainnya dengan menonaktifkan globbing sama sekali (denganset -f
) dan membelah (denganIFS=''
). Tetap:Tidak akan ada split + glob , tetapi jika
$var
kosong, alih-alih menerima satu argumen kosong, tidakcmd
akan menerima argumen sama sekali.Itu bisa menyebabkan bug (seperti yang sudah jelas
[ -n $var ]
). Itu mungkin dapat mematahkan harapan dan asumsi skrip dan menyebabkan kerentanan, tetapi saya tidak dapat memberikan contoh yang tidak terlalu jauh-baru saja diambil).Bagaimana bila Anda lakukan membutuhkan perpecahan + gumpal Operator?
Ya, itu biasanya ketika Anda ingin membiarkan variabel Anda tidak dikutip. Tetapi kemudian Anda perlu memastikan Anda menyetel split dan operator glob Anda dengan benar sebelum menggunakannya. Jika Anda hanya ingin bagian split dan bukan bagian glob (yang merupakan kasus sebagian besar waktu), maka Anda perlu menonaktifkan globbing (
set -o noglob
/set -f
) dan memperbaikinya$IFS
. Kalau tidak, Anda akan menyebabkan kerentanan juga (seperti contoh CGI David Korn yang disebutkan di atas).Kesimpulan
Singkatnya, meninggalkan variabel (atau perintah substitusi atau ekspansi aritmatika) tanpa tanda kutip di shell bisa sangat berbahaya memang terutama ketika dilakukan dalam konteks yang salah, dan sangat sulit untuk mengetahui mana konteks yang salah itu.
Itulah salah satu alasan mengapa itu dianggap praktik yang buruk .
Terima kasih sudah membaca sejauh ini. Jika itu melampaui kepala Anda, jangan khawatir. Orang tidak dapat mengharapkan semua orang memahami semua implikasi dari penulisan kode mereka seperti cara mereka menulisnya. Itu sebabnya kami memiliki rekomendasi praktik yang baik , sehingga dapat diikuti tanpa harus memahami mengapa.
(dan jika itu belum jelas, harap hindari menulis kode sensitif keamanan dalam shells).
Dan silahkan kutipan variabel Anda pada jawaban Anda di situs ini!
¹Dalam
ksh93
danpdksh
dan turunannya, ekspansi brace juga dilakukan kecuali globbing dinonaktifkan (dalam kasusksh93
versi hingga ksh93u +, bahkan ketikabraceexpand
opsi dinonaktifkan).sumber
[[
, hanya perbandingan RHS yang perlu dikutip:if [[ $1 = "$2" ]]; then
[[ $* = "$var" ]]
tidak sama dengan[[ "$*" = "$var" ]]
jika karakter pertama$IFS
bukan spasi denganbash
(dan jugamksh
jika$IFS
kosong meskipun dalam kasus itu saya tidak yakin apa$*
yang sama dengan, haruskah saya menaikkan bug?)).foo='bar; rm *'
, tidak, tidak akan, itu akan tetapi daftar isi direktori saat ini yang dapat dihitung sebagai pengungkapan informasi.print $foo
diksh93
( di manaprint
penggantian untukecho
yang mengatasi beberapa kekurangannya) memang memiliki kerentanan injeksi kode (misalnya denganfoo='-f%.0d z[0$(uname>&2)]'
) (Anda benar-benar perluprint -r -- "$foo"
.echo "$foo"
masih salah dan tidak dapat diperbaiki (meskipun umumnya kurang berbahaya)).[Terinspirasi oleh jawaban ini oleh cas .]
Tetapi bagaimana jika ...?
Saya tidak bisa mengatakan ; itu akan gagal jika string kosong.
grep "$ignorecase" other_grep_args
$ignorecase
Tanggapan:
Seperti dibahas dalam jawaban lain, ini masih akan gagal jika
IFS
berisi a-
atau ai
. Jika Anda memastikan bahwaIFS
tidak mengandung karakter apa pun di variabel Anda (dan Anda yakin bahwa variabel Anda tidak mengandung karakter gumpalan), maka ini mungkin aman.Tetapi ada cara yang lebih aman (meskipun agak jelek dan sangat tidak intuitif): gunakan
${ignorecase:+"$ignorecase"}
. Dari spesifikasi Bahasa Perintah POSIX Shell , di bawah 2.6.2 Parameter Ekspansi ,Kuncinya di sini, seperti apa adanya, adalah bahwa kita menggunakan
ignorecase
sebagaiparameter
dan"$ignorecase"
sebagaiword
. Jadi${ignorecase:+"$ignorecase"}
artinyaIni membawa kita ke tempat yang kita inginkan: jika variabel diatur ke string kosong, itu akan "dihapus" (seluruh ekspresi berbelit-belit ini tidak akan mengevaluasi apa pun - bahkan string kosong), dan jika variabel tersebut memiliki -Nilai kosong, kami mendapatkan nilai itu, dikutip.
Tetapi bagaimana jika ...?
Tanggapan:
Anda mungkin berpikir bahwa ini adalah kasus untuk digunakanTidak! Tahan godaan untuk berpikir untuk menggunakan dieval
.eval
sini.Sekali lagi, jika Anda telah memastikan bahwa
IFS
tidak mengandung karakter apa pun dalam variabel Anda (kecuali untuk spasi, yang Anda ingin dihormati), dan Anda yakin bahwa variabel Anda tidak mengandung karakter gumpalan, maka di atas mungkin adalah aman.Tapi, jika Anda menggunakan bash (atau ksh, zsh atau yash), ada cara yang lebih aman: gunakan array:
Dari bash (1) ,
Jadi
"${criteria[@]}"
memperluas (dalam contoh di atas) elemen nol, dua, atau empatcriteria
array, masing-masing dikutip. Secara khusus, jika tidak ada kondisi s yang benar,criteria
array tidak memiliki konten (seperti yang ditetapkan olehcriteria=()
pernyataan), dan tidak"${criteria[@]}"
mengevaluasi apa - apa (bahkan string kosong yang tidak nyaman).Ini menjadi sangat menarik dan rumit ketika Anda berurusan dengan beberapa kata, beberapa di antaranya adalah input dinamis (pengguna), yang sebelumnya tidak Anda ketahui, dan mungkin berisi spasi atau karakter khusus lainnya. Mempertimbangkan:
Catatan yang
$fname
dikutip setiap kali digunakan. Ini berfungsi bahkan jika pengguna memasukkan sesuatu sepertifoo bar
ataufoo*
;"${criteria[@]}"
mengevaluasi ke-name "foo bar"
atau-name "foo*"
. (Ingat bahwa setiap elemen array dikutip.)Array tidak berfungsi di semua shell POSIX; array adalah ksh / bash / zsh / yash-ism. Kecuali ... ada satu array yang didukung semua shell: daftar argumen, alias
"$@"
. Jika Anda selesai dengan daftar argumen yang Anda panggil (misalnya, Anda telah menyalin semua "parameter posisi" (argumen) ke dalam variabel, atau memprosesnya), Anda dapat menggunakan daftar arg sebagai array:The
"$@"
konstruk (yang, secara historis, datang pertama) memiliki semantik yang sama seperti - mengembang setiap argumen (yaitu, setiap elemen dari daftar argumen) untuk sebuah kata yang terpisah, seperti jika Anda telah mengetik ."${name[@]}"
"$1" "$2" "$3" …
Mengutip dari spesifikasi Bahasa Perintah POSIX Shell , di bawah 2.5.2 Parameter Khusus ,
Teks lengkapnya agak samar; titik kuncinya adalah bahwa ia menentukan yang
"$@"
akan menghasilkan bidang nol ketika tidak ada parameter posisi. Catatan sejarah: ketika"$@"
pertama kali diperkenalkan di shell Bourne (pendahulu untuk bash) pada tahun 1979, ia memiliki bug yang"$@"
digantikan oleh string kosong tunggal ketika tidak ada parameter posisi; lihat Apa${1+"$@"}
artinya dalam skrip shell, dan bagaimana bedanya"$@"
? , Keluarga Tradisional Bourne Shell , Apa${1+"$@"}
artinya ... dan di mana perlu? , dan"$@"
versus${1+"$@"}
.Array membantu dengan situasi pertama, juga:
____________________
PS (csh)
Ini harus dilakukan tanpa berkata, tetapi, untuk kepentingan orang-orang yang baru di sini: csh, tcsh, dll., Bukan cangkang Bourne / POSIX. Mereka keluarga yang sangat berbeda. Seekor kuda dengan warna berbeda. Seluruh permainan bola lainnya. Jenis kucing yang berbeda. Burung dari bulu lain. Dan, paling khusus, kaleng cacing yang berbeda.
Beberapa dari apa yang dikatakan di halaman ini berlaku untuk csh; seperti: itu adalah ide yang baik untuk mengutip semua variabel Anda kecuali Anda memiliki alasan yang baik untuk tidak melakukannya, dan Anda yakin Anda tahu apa yang Anda lakukan. Tapi, di csh, setiap variabel adalah array - Kebetulan hampir setiap variabel adalah array hanya dari satu elemen, dan bertindak sangat mirip dengan variabel shell biasa di shell Bourne / POSIX. Dan sintaksnya sangat berbeda (dan maksud saya sangat buruk ). Jadi kita tidak akan mengatakan apa-apa lagi tentang csh-family shells di sini.
sumber
csh
, Anda lebih suka menggunakan$var:q
daripada"$var"
yang terakhir tidak berfungsi untuk variabel yang berisi karakter baris baru (atau jika Anda ingin mengutip elemen-elemen array secara individual, daripada bergabung dengan spasi dengan argumen tunggal).bitrate="--bitrate 320"
bekerja dengan${bitrate:+$bitrate}
, danbitrate=--bitrate 128
tidak akan bekerja${bitrate:+"$bitrate"}
karena itu melanggar perintah. Apakah aman digunakan${variable:+$variable}
tanpa"
?$@
sesuatu yang lain), atau Anda hanya menolak menggunakan array karena alasan lain, saya merujuk Anda untuk "Seperti yang dibahas dalam jawaban lain". ... (Lanjutan)${variable:+$variable}
tanpa"
) akan gagal jika IFS mengandung-
,0
,2
,3
,a
,b
,e
,i
,r
ataut
.${bitrate:+$bitrate}
Saya ragu dengan jawaban Stéphane, namun ada kemungkinan untuk menyalahgunakan
$#
:atau $ ?:
Ini adalah contoh yang dibuat-buat, tetapi potensi memang ada.
sumber