Saya memiliki fungsi bash untuk mengatur $PATH
seperti ini -
assign-path()
{
str=$1
# if the $PATH is empty, assign it directly.
if [ -z $PATH ]; then
PATH=$str;
# if the $PATH does not contain the substring, append it with ':'.
elif [[ $PATH != *$str* ]]; then
PATH=$PATH:$str;
fi
}
Tetapi masalahnya adalah, saya harus menulis fungsi yang berbeda untuk variabel yang berbeda (misalnya, fungsi lain untuk $CLASSPATH
suka assign-classpath()
dll.). Saya tidak dapat menemukan cara untuk meneruskan argumen ke fungsi bash sehingga saya dapat mengaksesnya dengan referensi.
Akan lebih baik jika saya memiliki sesuatu seperti -
assign( bigstr, substr )
{
if [ -z bigstr ]; then
bigstr=substr;
elif [[ bigstr != *str* ]]; then
bigstr=bigstr:substr;
fi
}
Ada ide, bagaimana mencapai sesuatu seperti di atas di bash?
bash
bash-script
ramgorur
sumber
sumber
assign-path /abc
tidak akan menambahkan/abc
ke PATH jika $ PATH sudah berisi/abc/def
,/abcd
,/def/abc
dll Terutama Anda tidak dapat menambahkan/bin
jika PATH sudah berisi/usr/bin
.$PATH
dan uji meniadakan terhadap argumen Anda seperti:add=/bin dir=/usr/bin ; [ -z "${dir%"$add"}" ] || dir="${dir}:${add}"
. Dalam jawaban saya, saya melakukannya dengan argumen sebanyak yang Anda sukaIFS=:
.$PATH
? dan Tambahkan direktori$PATH
jika belum ada (di Super User ).Jawaban:
Di dalamnya
bash
Anda dapat menggunakan${!varname}
untuk memperluas variabel yang dirujuk oleh konten yang lain. Misalnya:Dari halaman manual:
Juga, untuk mengatur variabel yang dirujuk oleh konten (tanpa bahaya
eval
), Anda dapat menggunakandeclare
. Misalnya:Dengan demikian, Anda dapat menulis fungsi Anda seperti ini (hati-hati karena jika Anda menggunakan
declare
suatu fungsi, Anda harus memberi-g
atau variabelnya adalah lokal):Dan gunakan seperti:
Perhatikan bahwa saya juga telah memperbaiki bug di mana jika
substr
sudah menjadi substring dari salah satu anggota yang dipisahkan oleh titik duabigstr
, tetapi bukan anggota sendiri, maka itu tidak akan ditambahkan. Misalnya, ini akan memungkinkan penambahan/bin
kePATH
variabel yang sudah mengandung/usr/bin
. Ini menggunakanextglob
set untuk mencocokkan awal / akhir string atau titik dua kemudian apa pun. Tanpaextglob
, alternatifnya adalah:sumber
-g
dideclare
tidak tersedia dalam versi bash yang lebih lama, adakah cara agar saya dapat membuat ini kompatibel?export
untuk meletakkannya di lingkungan Anda (dengan risiko menimpa sesuatu yang penting) ataueval
(berbagai masalah termasuk keamanan jika Anda tidak berhati-hati). Jika menggunakaneval
Anda harus baik-baik saja jika Anda sukaeval "$target=\$substr"
. Jika Anda lupa\
, itu akan berpotensi mengeksekusi perintah jika ada ruang dalam kontensubstr
.Baru di bash 4.3, adalah
-n
opsi untukdeclare
&local
:Itu tercetak
hello, world
.sumber
Anda dapat menggunakan
eval
untuk mengatur parameter. Deskripsi perintah ini dapat ditemukan sini . Penggunaan berikuteval
salah:Sehubungan dengan evaluasi tambahan yang
eval
harus Anda gunakanPeriksa hasil menggunakan fungsi-fungsi ini:
Tetapi Anda dapat mencapai tujuan Anda tanpa menggunakan
eval
. Saya lebih suka cara ini yang lebih sederhana.Fungsi berikut membuat penggantian dengan cara yang benar (saya harap)
Periksa output berikut
Sekarang Anda dapat menggunakan
augment
fungsi dengan cara berikut untuk mengatur variabel:sumber
v='echo "OHNO!" ; var' ; l=val ; eval $v='$l'
- akan menggema "OHNO! Sebelum menugaskan var. Anda dapat" $ {v ## * [; "$ IFS"]} = '$ l' "untuk memastikan bahwa string tidak dapat diperluas ke apa pun yang tidak akan dievaluasi dengan =.=
pernyataan assignment. Anda bisa membantah bahwa skrip saya tidak memeriksa argumennya. Itu benar. Saya bahkan tidak memeriksa apakah ada argumen atau apakah jumlah argumen itu valid. Tapi ini dengan niat. OP dapat menambahkan cek seperti itu jika dia mau.v
(lebih baik nilainya) sebagai argumen kedua dari fungsi assign. Jadi nilainya harus di sisi kanan penugasan. Penting untuk mengutip argumen fungsiassign
. Saya menambahkan kehalusan ini ke posting saya.Dengan beberapa trik Anda dapat benar-benar mengirimkan parameter bernama ke fungsi, bersama dengan array (diuji dalam bash 3 dan 4).
Metode yang saya kembangkan memungkinkan Anda untuk mengakses parameter yang dikirimkan ke fungsi seperti ini:
Dengan kata lain, tidak hanya Anda dapat memanggil parameter Anda dengan nama mereka (yang merupakan inti yang lebih mudah dibaca), Anda sebenarnya dapat melewatkan array (dan referensi ke variabel - fitur ini hanya bekerja di bash 4.3)! Plus, variabel yang dipetakan semuanya dalam lingkup lokal, seperti $ 1 (dan lainnya).
Kode yang membuat pekerjaan ini cukup ringan dan bekerja baik di bash 3 dan bash 4 (ini adalah satu-satunya versi yang telah saya uji dengan). Jika Anda tertarik pada lebih banyak trik seperti ini yang membuat pengembangan dengan bash jauh lebih baik dan lebih mudah, Anda dapat melihat pada Bash Infinity Framework saya , kode di bawah ini dikembangkan untuk tujuan itu.
sumber
Apakah ini cocok?
sumber
eval
rentan terhadap eksekusi perintah sewenang-wenang.eval
saluran lebih aman? Biasanya, ketika saya pikir saya perlu eval, saya memutuskan untuk tidak menggunakan * sh dan beralih ke bahasa lain. Di sisi lain, menggunakan ini dalam skrip untuk menambahkan entri ke beberapa variabel seperti PATH, itu akan berjalan dengan konstanta string dan bukan input pengguna ...eval
dengan aman - tetapi butuh banyak pemikiran. Jika Anda hanya mencoba mereferensikan suatu parameter, Anda ingin melakukan sesuatu seperti ini: denganeval "$1=\"\$2\""
cara itu padaeval's
pass pertama, ia hanya mengevaluasi $ 1 dan pada detik itu bernilai = "$ 2". Tetapi Anda perlu melakukan sesuatu yang lain - ini tidak perlu di sini."${1##*[;"$IFS"]}=\"\$2\""
- dan bahkan itu tanpa jaminan. Ataueval "$(set -- $1 ; shift $(($#-1)) ; echo $1)=\"\$2\""
. Ini tidak mudah.Argumen yang dinamai bukan bagaimana sintaks Bash dirancang. Bash dirancang untuk menjadi perbaikan berulang pada kulit Bourne. Karena itu perlu memastikan hal-hal tertentu bekerja antara dua cangkang sebanyak mungkin. Jadi itu tidak dimaksudkan untuk menjadi lebih mudah untuk skrip dengan keseluruhan, itu hanya dimaksudkan untuk menjadi lebih baik daripada Bourne sambil memastikan bahwa jika Anda mengambil skrip dari lingkungan Bourne ke
bash
semudah mungkin. Itu tidak sepele karena banyak cangkang masih memperlakukan Bourne sebagai standar de facto. Karena orang menulis skrip mereka agar kompatibel dengan Bourne (untuk portabilitas ini) kebutuhan tetap berlaku dan tidak mungkin untuk pernah berubah.Anda mungkin lebih baik melihat skrip shell yang berbeda (seperti
python
atau sesuatu) sepenuhnya jika itu layak. Jika Anda menghadapi keterbatasan bahasa, Anda harus mulai menggunakan bahasa baru.sumber
bash
kehidupan ini benar. Tetapi sekarang ketentuan khusus dibuat. Variabel referensi lengkap sekarang tersedia dibash 4.3
- lihat jawaban derobert .Dengan
sh
sintaks standar (akan berfungsibash
, dan tidak hanya dibash
), Anda dapat melakukan:Seperti untuk solusi menggunakan
bash
'sdeclare
, itu aman selama$1
mengandung nama variabel yang valid.sumber
TENTANG ARGS NAMED:
Ini sangat sederhana dilakukan dan
bash
tidak diperlukan sama sekali - ini adalah perilaku penugasan POSIX dasar yang ditentukan melalui ekspansi parameter:Untuk melakukan demo dengan nada yang mirip dengan @Graeme, tetapi dengan cara yang portabel:
Dan di sana saya hanya melakukan
str=
untuk memastikan ia memiliki nilai nol, karena ekspansi parameter memiliki perlindungan bawaan terhadap penetapan ulang lingkungan shell inline jika sudah ditetapkan.LARUTAN:
Untuk masalah spesifik Anda, saya tidak percaya bahwa argumen bernama diperlukan, meskipun tentu saja mungkin. Gunakan
$IFS
sebaliknya:Inilah yang saya dapatkan ketika saya menjalankannya:
Perhatikan itu hanya menambahkan argumen yang belum ada
$PATH
atau yang datang sebelumnya? Atau bahkan butuh lebih dari satu argumen?$IFS
berguna.sumber
assign
sini. Jika Anda memiliki pertanyaan tentang cara kerjanya, saya akan dengan senang hati menjawab. Omong-omong, jika Anda benar-benar ingin menamai argumen, Anda mungkin ingin melihat jawaban saya yang lain ini di mana saya menunjukkan cara mendeklarasikan fungsi yang dinamai argumen fungsi lain: unix.stackexchange.com/a/120531/52934Saya tidak dapat menemukan apa pun seperti ruby, python, dll. Tetapi ini terasa lebih dekat dengan saya
Keterbacaan menurut saya lebih baik, 4 baris berlebihan untuk menyatakan nama parameter Anda. Terlihat lebih dekat dengan bahasa modern juga.
sumber
a=$1 b=$2 ...
bekerja dengan baik.