Dalam python kita dapat menghias fungsi dengan kode yang secara otomatis diterapkan dan dieksekusi terhadap fungsi.
Apakah ada fitur serupa di bash?
Dalam skrip yang sedang saya kerjakan, saya memiliki beberapa pelat yang menguji argumen yang diperlukan dan keluar jika tidak ada - dan menampilkan beberapa pesan jika bendera debug ditentukan.
Sayangnya saya harus memasukkan kembali kode ini ke setiap fungsi dan jika saya ingin mengubahnya, saya harus memodifikasi setiap fungsi.
Apakah ada cara untuk menghapus kode ini dari setiap fungsi dan menerapkannya ke semua fungsi, mirip dengan dekorator dengan python?
Jawaban:
Itu akan jauh lebih mudah dengan
zsh
yang memiliki fungsi anonim dan array asosiatif khusus dengan kode fungsi. Denganbash
namun Anda bisa melakukan sesuatu seperti:Yang akan menghasilkan:
Anda tidak dapat memanggil hiasi dua kali untuk menghias fungsi Anda dua kali.
Dengan
zsh
:sumber
typeset
perlu? Bukankah itu akan menyatakan sebaliknya?eval "_inner_$(typeset -f x)"
menciptakan_inner_x
sebagai salinan yang aslix
(sama sepertifunctions[_inner_x]=$functions[x]
dizsh
).return
.Saya sudah membahas bagaimana dan mengapa metode di bawah ini berfungsi pada beberapa kesempatan sebelumnya sehingga saya tidak akan melakukannya lagi. Secara pribadi, favorit saya sendiri pada topik ada di sini dan di sini .
Jika Anda tidak tertarik membaca itu tetapi masih penasaran, pahami saja bahwa dokumen di sini yang dilampirkan pada input fungsi dievaluasi untuk ekspansi shell sebelum fungsi tersebut berjalan, dan bahwa mereka dihasilkan kembali dalam keadaan seperti ketika fungsi tersebut didefinisikan. setiap kali fungsi dipanggil.
MENYATAKAN
Anda hanya perlu fungsi yang mendeklarasikan fungsi lainnya.
MENJALANKANNYA
Di sini saya meminta
_fn_init
untuk mendeklarasikan saya sebuah fungsi yang dipanggilfn
.YG DIBUTUHKAN
Jika saya ingin memanggil fungsi ini akan mati kecuali variabel lingkungan
_if_unset
diatur.Harap perhatikan urutan jejak shell - tidak hanya
fn
gagal ketika dipanggil saat_if_unset
tidak disetel, tetapi juga tidak pernah berjalan di tempat pertama . Ini adalah faktor yang paling penting untuk dipahami ketika bekerja dengan ekspansi dokumen-di sini - mereka harus selalu terjadi terlebih dahulu karena mereka<<input
semua.Kesalahan berasal dari
/dev/fd/4
karena shell induk mengevaluasi input itu sebelum menyerahkannya ke fungsi. Ini adalah cara paling sederhana dan paling efisien untuk menguji lingkungan yang diperlukan.Bagaimanapun, kegagalannya mudah diatasi.
FLEKSIBEL
Variabel
common_param
dievaluasi ke nilai default pada input untuk setiap fungsi yang dideklarasikan oleh_fn_init
. Tetapi nilai itu juga dapat diubah ke yang lain yang juga akan dihormati oleh setiap fungsi yang dinyatakan secara serupa. Saya akan meninggalkan jejak shell sekarang - kita tidak akan pergi ke wilayah yang belum dipetakan di sini atau apa pun.Di atas saya mendeklarasikan dua fungsi dan set
_if_unset
. Sekarang, sebelum memanggil salah satu fungsi, saya akan menghapusnyacommon_param
sehingga Anda dapat melihat mereka akan mengaturnya sendiri ketika saya memanggil mereka.Dan sekarang dari ruang lingkup penelepon:
Tapi sekarang saya ingin semuanya menjadi sesuatu yang lain:
Dan jika saya tidak disetel
_if_unset
?RESET
Jika Anda perlu mengatur ulang status fungsi kapan saja hal itu mudah dilakukan. Anda hanya perlu melakukan (dari dalam fungsi):
Saya menyimpan argumen yang digunakan untuk mendeklarasikan fungsi pada
5<<\RESET
file-descriptor input. Jadi.dot
sumber yang ada di shell kapan saja akan mengulangi proses yang mengaturnya di tempat pertama. Ini semua sangat mudah, benar-benar, dan cukup portabel sepenuhnya jika Anda bersedia mengabaikan fakta bahwa POSIX tidak benar-benar menentukan jalur simpul perangkat deskriptor file (yang merupakan keharusan bagi shell.dot
).Anda dapat dengan mudah memperluas perilaku ini dan mengonfigurasi berbagai status untuk fungsi Anda.
LEBIH?
Omong-omong, ini nyaris tidak menggores permukaan. Saya sering menggunakan teknik ini untuk menanamkan fungsi pembantu kecil yang dapat dideklarasikan kapan saja ke dalam input fungsi utama - misalnya, untuk
$@
array posisi tambahan yang diperlukan. Bahkan - seperti yang saya yakini, pasti ada sesuatu yang sangat dekat dengan ini yang dilakukan oleh shell dengan urutan lebih tinggi. Anda dapat melihat mereka secara mudah diberi nama pemrograman.Saya juga ingin mendeklarasikan fungsi generator yang menerima tipe parameter terbatas dan kemudian mendefinisikan fungsi burner sekali pakai atau fungsi lingkup terbatas sepanjang garis lambda - atau fungsi in-line - yang hanya ada
unset -f
saat melalui. Anda dapat melewati fungsi shell di sekitar.sumber
eval
?.dot
berfungsi dengan file dan stream sehingga Anda tidak mengalami masalah daftar argumen yang sama dengan yang Anda mungkin sebaliknya. Tetap saja, itu mungkin masalah preferensi. Saya tentu berpikir itu lebih bersih - terutama ketika Anda masuk ke eval eval - itu adalah mimpi buruk dari tempat saya duduk..dot
sumber sampai Anda baik dan siap - atau selamanya. Ini memungkinkan Anda sedikit lebih bebas dalam menguji evaluasinya. Dan itu memberikan fleksibilitas keadaan input - yang dapat ditangani dengan cara lain - tetapi jauh lebih berbahaya dari perspektif itueval
.Saya pikir salah satu cara untuk mencetak informasi tentang fungsi, ketika Anda
adalah mengubah bash builtin
return
dan / atauexit
di awal setiap skrip (atau dalam beberapa file, yang Anda sumber setiap kali sebelum menjalankan program). Jadi, Anda mengetikJika Anda menjalankan ini, Anda akan mendapatkan:
Itu mungkin mudah diperbarui dengan bendera debugging jika Anda perlu, agak seperti ini:
Pernyataan cara ini akan dieksekusi hanya ketika variabel VERBOSE diatur (setidaknya itulah cara saya menggunakan verbose dalam skrip saya). Ini tentu tidak menyelesaikan masalah fungsi dekorasi, tetapi dapat menampilkan pesan jika fungsi mengembalikan status tidak nol.
Demikian pula Anda dapat mendefinisikan ulang
exit
, dengan mengganti semua instancereturn
, jika Anda ingin keluar dari skrip.EDIT: Saya ingin menambahkan di sini cara saya gunakan untuk menghias fungsi di bash, jika saya punya banyak dari mereka dan yang bersarang juga. Ketika saya menulis skrip ini:
Dan untuk hasilnya saya bisa mendapatkan ini:
Dapat bermanfaat bagi seseorang yang memiliki fungsi dan ingin men-debugnya, untuk melihat kesalahan fungsi yang terjadi. Ini didasarkan pada tiga fungsi, yang dapat dijelaskan di bawah ini:
Saya mencoba untuk menempatkan sebanyak mungkin dalam komentar, tapi di sini juga deskripsi: Saya menggunakan
_ ()
fungsi sebagai dekorator, yang saya diletakkan setelah deklarasi setiap fungsi:foo () { _
. Fungsi ini mencetak nama fungsi dengan lekukan yang tepat, tergantung seberapa dalam fungsi dalam fungsi lain (sebagai lekukan standar saya menggunakan 4 jumlah spasi). Saya biasanya mencetak ini dalam warna abu-abu, untuk memisahkan ini dari cetakan yang biasa. Jika fungsi perlu didekorasi dengan argumen, atau tanpa argumen, seseorang dapat memodifikasi baris pra-terakhir dalam fungsi dekorator.Untuk mencetak sesuatu di dalam fungsi, saya memperkenalkan
print ()
fungsi yang mencetak semua yang diteruskan dengan indentasi yang tepat.Fungsi
set_indentation_for_print_function
tidak persis apa artinya, menghitung lekukan dari${FUNCNAME[@]}
array.Cara ini memiliki beberapa kekurangan, misalnya seseorang tidak dapat melewatkan opsi untuk
print
sukaecho
, misalnya-n
atau-e
, dan juga jika fungsi mengembalikan 1, itu tidak dihiasi. Dan juga untuk argumen, diteruskan keprint
lebih dari lebar terminal, yang akan dibungkus di layar, orang tidak akan melihat lekukan untuk garis yang dibungkus.Cara terbaik untuk menggunakan dekorator ini adalah dengan meletakkannya di file terpisah dan di setiap skrip baru untuk sumber file ini
source ~/script/hand_made_bash_functions.sh
.Saya pikir cara terbaik untuk menggabungkan fungsi dekorator dalam bash, adalah dengan menulis dekorator dalam tubuh masing-masing fungsi. Saya pikir itu jauh lebih mudah untuk menulis fungsi di dalam fungsi dalam bash, karena memiliki opsi untuk mengatur semua variabel global, tidak seperti di Bahasa Berorientasi Objek standar. Itu membuatnya seolah-olah Anda menempatkan label di sekitar kode Anda di bash. Setidaknya itu membantu saya untuk skrip debugging.
sumber
Mungkin contoh dekorator di proyek http://sourceforge.net/projects/oobash/ dapat membantu Anda (oobash / docs / example / decorator.sh).
sumber
Bagi saya ini terasa seperti cara paling sederhana untuk menerapkan pola dekorator di dalam bash.
sumber
"$@"
).Saya melakukan banyak (mungkin terlalu banyak :)) metaprogramming di Bash, dan telah menemukan dekorator berharga untuk menerapkan kembali perilaku dengan cepat. Saya pesta-cache yang menggunakan perpustakaan dekorasi untuk transparan memoize fungsi Bash dengan upacara minimal:
Jelas
bc::cache
melakukan lebih dari sekadar mendekorasi, tetapi dekorasi yang mendasarinya bergantung padabc::copy_function
menyalin fungsi yang ada ke nama baru, sehingga fungsi aslinya dapat ditimpa dengan dekorator.Berikut adalah contoh sederhana dari dekorator yang
time
merupakan fungsi yang didekorasi, menggunakanbc::copy_function
:Demo:
sumber