Standar POSIX memaksakan ekspansi kata untuk dilakukan dalam urutan berikut (menekankan milikku):
Ekspansi Tilde (lihat Ekspansi Tilde), ekspansi parameter (lihat Ekspansi Parameter), substitusi perintah (lihat Substitusi Perintah), dan ekspansi aritmatika (lihat Ekspansi Aritmatika) harus dilakukan, mulai dari awal hingga akhir. Lihat item 5 dalam Pengakuan Token.
Pemisahan bidang (lihat Pemisahan Bidang) harus dilakukan pada bagian-bagian dari bidang yang dihasilkan oleh langkah 1, kecuali IFS adalah nol.
Perluasan Pathname (lihat Perluasan Pathname) harus dilakukan, kecuali set -f berlaku.
Penghapusan kutipan (lihat Penghapusan Penawaran) akan selalu dilakukan terakhir.
Satu-satunya poin yang menarik bagi kami di sini adalah yang pertama: seperti yang Anda lihat ekspansi tilde diproses sebelum ekspansi parameter:
- Shell mencoba ekspansi tilde aktif
echo $x
, tidak ada tilde yang ditemukan, jadi ia melanjutkan.
- Shell mencoba ekspansi parameter pada
echo $x
, $x
ditemukan dan diperluas dan baris perintah menjadi echo ~/someDirectory
.
- Pemrosesan berlanjut, ekspansi tilde telah diproses
~
karakter tetap apa adanya.
Dengan menggunakan tanda kutip saat menetapkan $x
, Anda secara eksplisit meminta untuk tidak memperluas tilde dan memperlakukannya seperti karakter normal. Suatu hal yang sering terlewatkan adalah bahwa dalam perintah shell Anda tidak perlu mengutip seluruh string, sehingga Anda dapat membuat ekspansi terjadi saat penugasan variabel:
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Dan Anda juga dapat membuat ekspansi terjadi pada echo
command-line selama itu bisa terjadi sebelum ekspansi parameter:
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Jika karena alasan tertentu Anda benar-benar perlu memengaruhi tilde ke $x
variabel tanpa ekspansi, dan dapat memperluasnya pada echo
perintah, Anda harus melanjutkan dua kali untuk memaksa dua ekspansi $x
variabel terjadi:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Namun, perlu diketahui bahwa tergantung pada konteks di mana Anda menggunakan struktur seperti itu mungkin memiliki efek samping yang tidak diinginkan. Sebagai aturan praktis, lebih memilih untuk menghindari menggunakan apa pun yang diperlukan eval
ketika Anda memiliki cara lain.
Jika Anda ingin secara khusus mengatasi masalah tilde yang bertentangan dengan segala jenis ekspansi lainnya, struktur seperti itu akan lebih aman dan portabel:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Struktur ini secara eksplisit memeriksa keberadaan arahan ~
dan menggantinya dengan dir home user jika ditemukan.
Mengikuti komentar Anda, x="${HOME}/${x#"~/"}"
mungkin memang mengejutkan bagi seseorang yang tidak digunakan dalam pemrograman shell, tetapi sebenarnya terkait dengan aturan POSIX yang sama seperti yang saya kutip di atas.
Seperti yang diberlakukan oleh standar POSIX, penghapusan penawaran terjadi terakhir dan ekspansi parameter terjadi sangat awal. Dengan demikian, ${#"~"}
dievaluasi dan diperluas jauh sebelum evaluasi kutipan luar. Secara bergantian, sebagaimana didefinisikan dalam aturan ekspansi Parameter :
Dalam setiap kasus bahwa nilai kata diperlukan (berdasarkan status parameter, seperti yang dijelaskan di bawah), kata harus mengalami ekspansi tilde, ekspansi parameter, substitusi perintah, dan ekspansi aritmatika.
Dengan demikian, sisi kanan #
operator harus dikutip atau melarikan diri dengan benar untuk menghindari ekspansi tilde.
Jadi, untuk menyatakannya secara berbeda, ketika penafsir shell melihat x="${HOME}/${x#"~/"}"
, ia melihat:
${HOME}
dan ${x#"~/"}
harus diperluas.
${HOME}
diperluas ke konten $HOME
variabel.
${x#"~/"}
memicu ekspansi bersarang: "~/"
diuraikan tetapi, dikutip, diperlakukan sebagai literal 1 . Anda bisa menggunakan tanda kutip tunggal di sini dengan hasil yang sama.
${x#"~/"}
ekspresi itu sendiri sekarang diperluas, menghasilkan awalan ~/
yang dihapus dari nilai $x
.
- Hasil di atas sekarang disatukan: ekspansi
${HOME}
, literal /
, ekspansi ${x#"~/"}
.
- Hasil akhirnya terlampir dalam tanda kutip ganda, yang secara fungsional mencegah pemisahan kata. Saya katakan fungsional di sini karena ini tanda kutip ganda secara teknis tidak diperlukan (lihat di sini dan di sana misalnya), tetapi sebagai gaya pribadi segera sebagai tugas mendapat apa pun di luar
a=$b
saya biasanya merasa lebih jelas add tanda kutip ganda.
By-the-way, jika melihat lebih dekat dengan case
sintaks, Anda akan melihat "~/"*
konstruksi yang bergantung pada konsep yang sama seperti yang x=~/'someDirectory'
saya jelaskan di atas (di sini sekali lagi, tanda kutip ganda dan sederhana dapat digunakan secara bergantian).
Jangan khawatir jika hal-hal ini mungkin tampak tidak jelas pada pandangan pertama (mungkin bahkan pada pandangan kedua atau selanjutnya!). Menurut pendapat saya, ekspansi parameter adalah, dengan subkulit, salah satu konsep yang paling kompleks untuk dipahami saat pemrograman dalam bahasa shell.
Saya tahu bahwa beberapa orang mungkin sangat tidak setuju, tetapi jika Anda ingin belajar pemrograman shell lebih mendalam, saya mendorong Anda untuk membaca Panduan Skrip Bash Lanjutan : mengajarkan skrip Bash, jadi dengan banyak ekstensi dan lonceng-dan- peluit dibandingkan dengan scripting shell POSIX, tapi saya menemukan itu ditulis dengan baik dengan banyak contoh praktis. Setelah Anda mengelola ini, mudah untuk membatasi diri Anda ke fitur POSIX ketika Anda perlu, saya pribadi berpikir bahwa memasukkan langsung di bidang POSIX adalah kurva belajar curam yang tidak perlu untuk pemula (bandingkan penggantian tilde POSIX saya dengan Bash seperti regex @ m0dular's seperti Bash setara untuk mendapatkan ide tentang apa yang saya maksud;)!).
1 : Yang menuntun saya menemukan bug di Dash yang tidak menerapkan ekspansi tilde di sini dengan benar (penggunaan yang dapat diverifikasi x='~/foo'; echo "${x#~/}"
). Ekspansi parameter adalah bidang yang kompleks untuk pengguna dan pengembang shell itu sendiri!
x='~'; print -l ${x} ${~x}
. Saya menyerah setelah menggalibash
manual untuk sementara waktu.