Ketika saya membuka bash prompt dan ketik:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
Saya berharap bahwa baris ke-5 di atas akan pergi + echo /home/myUsername/someDirectory
. Apakah ada cara untuk melakukan ini? Dalam skrip Bash asli saya, variabel x sebenarnya sedang diisi dari data dari file input, melalui loop seperti ini:
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
Meski begitu, saya mendapatkan hasil yang serupa, dengan echo '~/someDirectory'
bukannya echo /home/myUsername/someDirectory
.
x='~'; print -l ${x} ${~x}
. Saya menyerah setelah menggalibash
manual untuk sementara waktu.Jawaban:
Standar POSIX memaksakan ekspansi kata untuk dilakukan dalam urutan berikut (menekankan milikku):
Satu-satunya poin yang menarik bagi kami di sini adalah yang pertama: seperti yang Anda lihat ekspansi tilde diproses sebelum ekspansi parameter:
echo $x
, tidak ada tilde yang ditemukan, jadi ia melanjutkan.echo $x
,$x
ditemukan dan diperluas dan baris perintah menjadiecho ~/someDirectory
.~
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:Dan Anda juga dapat membuat ekspansi terjadi pada
echo
command-line selama itu bisa terjadi sebelum ekspansi parameter:Jika karena alasan tertentu Anda benar-benar perlu memengaruhi tilde ke
$x
variabel tanpa ekspansi, dan dapat memperluasnya padaecho
perintah, Anda harus melanjutkan dua kali untuk memaksa dua ekspansi$x
variabel terjadi: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:
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 :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
.${HOME}
, literal/
, ekspansi${x#"~/"}
.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 yangx=~/'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!sumber
x="${HOME}/${x#"~/"}"
? Ini terlihat seperti gabungan dari 3 string:"${HOME}/${x#"
,~/
, dan"}"
. Apakah shell memungkinkan untuk tanda kutip ganda bersarang ketika pasangan dalam tanda kutip ganda berada di dalam${ }
blok?Satu jawaban yang mungkin:
Karena Anda membaca input dari file, saya tidak akan melakukan ini.
Anda dapat mencari dan mengganti ~ dengan nilai $ HOME, seperti ini:
Memberi saya:
sumber
${parameter/pattern/string}
ekspansi adalah ekstensi Bash dan mungkin tidak tersedia di shell lain.case
struktur POSIX saya menggambarkan dengan baik bagaimana skrip Bash lebih ramah pengguna khususnya untuk pemula.