Saya memiliki fungsi-fungsi ini di ~/.bashrc
:
function guard() {
if [ -e 'Gemfile' ]; then
bundle exec guard "$@"
else
command guard "$@"
fi
}
function rspec() {
if [ -e 'Gemfile' ]; then
bundle exec rspec "$@"
else
command rspec "$@"
fi
}
function rake() {
if [ -e 'Gemfile' ]; then
bundle exec rake "$@"
else
command rake "$@"
fi
}
Seperti yang Anda lihat, fungsi-fungsi ini sangat mirip. Saya ingin mendefinisikan 3 fungsi ini sekaligus. Apakah ada cara untuk membuatnya?
lingkungan Hidup
bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
for loop?
maksud saya, variabel yang dideklarasikan secarafor loop
umum menghilang - saya harapkan fungsi yang sama untuk alasan yang sama.bash -c 'for i in 1; do :; done; echo $i'
=>1
. Thetype
jelas menunjukkan bahwa fungsi ada di luar lingkup loop.bash
pelingkupan dinamis yang bisa Anda dapatkan adalahlocal
variabel lokal untuk ruang lingkup seluruh fungsi , variabel pasti tidak "menghilang" setelah satu loop. Bahkan, karena tidak ada fungsi yang terlibat di sini, bahkan tidak mungkin untuk mendefinisikanlocal
variabel dalam kasus ini.Surat wasiat di atas
. source /dev/fd/3
yang dimasukkan ke dalam_gem_dec()
fungsi setiap kali disebut sebagai satu-here-document. _gem_dec's
satunya pekerjaan yang dievaluasi adalah untuk menerima satu parameter dan pra-evaluasi itu sebagaibundle exec
target dan sebagai nama fungsi di mana ia ditargetkan.NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
Namun dalam kasus di atas, saya pikir tidak ada risiko.
Jika blok-kode di atas disalin ke
.bashrc
file, shell tidak hanya akan berfungsi_guard(), _rspec()
dan_rake()
dideklarasikan saat login, tetapi_gem_dec()
fungsi tersebut juga akan tersedia untuk dieksekusi kapan saja di prompt shell Anda (atau sebaliknya) sehingga fungsi templated baru dapat dideklarasikan kapan pun Anda suka hanya dengan:Dan terima kasih kepada @Andrew karena menunjukkan kepada saya bahwa ini tidak akan dimakan oleh
for loop.
TAPI BAGAIMANA CARANYA?
Saya menggunakan
3
deskriptor file di atas untuk tetapstdin, stdout, and stderr, or <&0 >&1 >&2
terbuka dari kebiasaan - meskipun, seperti juga kasus untuk beberapa tindakan pencegahan standar lainnya yang saya terapkan di sini - karena fungsi yang dihasilkan sangat sederhana, itu benar-benar tidak perlu. Ini adalah praktik yang baik. Memanggilshift $#
adalah tindakan pencegahan lain yang tidak perlu.Namun, ketika file ditentukan sebagai
<input
atau>output
dengan[optional num]<file
atau[optional num]>file
pengalihan kernel membacanya menjadi deskriptor file, yang dapat diakses melaluicharacter device
file khusus di/dev/fd/[0-9]*
. Jika[optional num]
specifier dihilangkan, maka0<file
diasumsikan untuk input dan1>file
untuk output. Pertimbangkan ini:Dan karena a
here-document
hanya sarana untuk menggambarkan file inline dalam blok-kode, ketika kita melakukannya:Kita mungkin juga melakukan:
Dengan satu perbedaan yang sangat penting . Jika Anda tidak
"'\quote'"
yang<<"'\LIMITER"'
darihere-document
maka shell akan mengevaluasi untuk shell$expansion
seperti:Jadi, untuk
_gem_dec()
, yang3<<-FUNC here-document
dievaluasi sebagai file input, sama seperti itu akan jika itu3<~/some.file
kecuali bahwa karena kita meninggalkanFUNC
limiter bebas dari kutipan, itu pertama dievaluasi untuk$expansion.
Yang penting tentang ini adalah bahwa itu adalah input, artinya itu hanya ada untuk_gem_dec(),
tetapi itu juga dievaluasi sebelum_gem_dec()
fungsi berjalan karena shell kita harus membaca dan mengevaluasi$expansions
sebelum menyerahkannya sebagai input.Mari kita lakukan
guard,
misalnya:Jadi pertama-tama shell harus menangani input, yang berarti membaca:
Ke deskriptor file 3 dan mengevaluasinya untuk ekspansi shell. Jika saat ini Anda berlari:
Atau:
Karena keduanya adalah perintah yang setara, Anda akan melihat *:
... sebelum kode apa pun dalam fungsi dijalankan sama sekali. Ini adalah fungsi ini
<input
, setelah semua. Untuk lebih banyak contoh, lihat jawaban saya untuk pertanyaan berbeda di sini .(* Secara teknis ini tidak sepenuhnya benar. Karena saya menggunakan yang memimpin
-dash
sebelumhere-doc limiter
, semua di atas akan dibenarkan. Tapi saya menggunakan yang-dash
saya bisa<tab-insert>
untuk keterbacaan di tempat pertama sehingga saya tidak akan mengupas<tab-inserts>
sebelumnya) menawarkannya kepada Anda untuk dibaca ...)Bagian terbaik tentang ini adalah kutipan - pemberitahuan bahwa
'"
tanda kutip tetap dan hanya\
tanda kutip yang dilucuti. Mungkin karena alasan ini lebih dari yang lain bahwa jika Anda harus mengevaluasi shell dua kali,$expansion
saya akan merekomendasikanhere-document
karena penawarannya jauh lebih mudah daripadaeval
.Lagi pula, sekarang kode di atas persis seperti file yang dimasukkan seperti
3<~/heredoc.file
hanya menunggu_gem_dec()
fungsi untuk pergi dan menerima inputnya/dev/fd/3
.Jadi ketika kita memulai
_gem_dec()
hal pertama yang saya lakukan adalah melemparkan semua parameter posisi, karena langkah kita selanjutnya adalah ekspansi shell yang dievaluasi dua kali dan saya tidak ingin semua yang ada$expansions
ditafsirkan sebagai salah satu$1 $2 $3...
parameter saya saat ini . Jadi saya:shift
buang sebanyak yangpositional parameters
Anda tentukan dan mulai dari$1
yang tersisa. Jadi jika saya menelepon_gem_dec one two three
pada prompt_gem_dec's $1 $2 $3
parameter posisi akanone two three
dan jumlah total posisi saat ini, atau$#
akan 3. Jika saya kemudian disebutshift 2,
nilai-nilaione
dantwo
akanshift
ed jauh, nilai$1
akan berubah menjadithree
dan$#
akan memperluas untuk 1. Jadishift $#
hanya membuang mereka semua. Melakukan ini benar-benar pencegahan dan hanya kebiasaan yang telah saya kembangkan setelah melakukan hal semacam ini untuk sementara waktu. Di sini(subshell)
tersebar sedikit demi kejelasan:Bagaimanapun, langkah selanjutnya adalah di mana keajaiban terjadi. Jika Anda
. ~/some.sh
di prompt shell maka semua fungsi dan variabel lingkungan yang dideklarasikan di~/some.sh
kemudian akan dipanggil di prompt shell Anda. Hal yang sama berlaku di sini, kecuali kita. source
yangcharacter device
file khusus untuk file descriptor kami, atau. /dev/fd/3
- yang mana kamihere-document
file dalam-line telah pathed - dan kami telah menyatakan fungsi kita. Dan begitulah cara kerjanya.Sekarang lakukan apa pun
_guard
yang seharusnya dilakukan fungsi Anda .Tambahan:
Cara yang bagus untuk mengatakan simpan posisi Anda:
EDIT:
Ketika saya pertama kali menjawab pertanyaan ini, saya lebih fokus pada masalah mendeklarasikan sebuah shell yang
function()
mampu mendeklarasikan fungsi-fungsi lain yang akan tetap ada di$ENV
iron shell saat ini daripada yang saya lakukan pada apa yang penanya akan lakukan dengan fungsi-fungsi persisten tersebut. Sejak itu saya menyadari bahwa solusi yang saya tawarkan pada awalnya3<<-FUNC
berupa:Akan kemungkinan besar tidak bekerja seperti yang diharapkan untuk penanya karena saya secara khusus mengubah nama fungsi deklaratif ini dari
$1
ke_${1}
mana, jika disebut seperti_gem_dec guard
misalnya, akan menghasilkan_gem_dec
mendeklarasikan fungsi bernama_guard
sebagai lawan hanyaguard
.Catatan: Perilaku seperti itu merupakan kebiasaan bagi saya - saya biasanya beroperasi dengan anggapan bahwa fungsi shell harus menempati hanya milik mereka sendiri
_namespace
untuk menghindari intrusi mereka padanamespace
shell yangcommands
tepat.Namun, ini bukan kebiasaan universal, sebagaimana diperlihatkan dalam penggunaan penanya
command
untuk dipanggil$1
.Pemeriksaan lebih lanjut membuat saya percaya hal-hal berikut:
Ini tidak akan berhasil sebelumnya karena saya juga mengubah yang
$1
dipanggil olehcommand
untuk membaca:Yang tidak akan menghasilkan eksekusi
ruby
fungsi yang dikompilasi oleh fungsi shell sebagai:Saya harap Anda dapat melihat (seperti yang akhirnya saya lakukan) bahwa tampaknya penanya hanya menggunakan
command
sama sekali untuk menentukan secara tidak langsungnamespace
karenacommand
akan lebih suka memanggil file yang dapat dieksekusi di$PATH
atas fungsi shell dengan nama yang sama.Jika analisis saya benar (karena saya harap penanya akan mengonfirmasi) maka ini:
Seharusnya lebih baik memenuhi persyaratan tersebut dengan pengecualian bahwa panggilan
guard
pada prompt hanya akan mencoba untuk mengeksekusi file yang dapat dieksekusi dalam$PATH
namaguard
sedangkan panggilan_guard
pada prompt akan memeriksaGemfile's
keberadaan dan mengkompilasi sesuai atau mengeksekusiguard
dieksekusi di$PATH
. Dengan caranamespace
ini dilindungi dan, setidaknya saat saya melihatnya, niat si penanya masih terpenuhi.Bahkan, menganggap fungsi shell kami
_${1}()
dan dieksekusi${PATH}/${1}
adalah hanya dua cara shell kami bisa menafsirkan panggilan baik$1
atau_${1}
maka penggunaancommand
fungsi sama sekali sekarang seluruhnya terbuat berlebihan. Namun, saya membiarkannya tetap karena saya tidak suka melakukan kesalahan yang sama dua kali ... berturut-turut.Jika ini tidak dapat diterima oleh si penanya dan dia lebih suka untuk menghilangkan
_
sama sekali maka, dalam bentuk saat ini, mengedit_underscore
keluar harus semua penanya perlu lakukan untuk memenuhi persyaratannya seperti yang saya mengerti.Selain perubahan yang saya juga telah mengedit fungsi untuk menggunakan
&&
dan / atau||
shell arus pendek conditional daripada aslinyaif/then
sintaks. Dengan cara inicommand
pernyataan hanya dievaluasi sama sekali jikaGemfile
tidak ada$PATH
. Modifikasi ini memang membutuhkan penambahanreturn $?
namun untuk memastikanbundle
pernyataan tidak berjalan jikaGemfile
tidak ada tetapiruby $1
fungsi mengembalikan apa pun selain 0.Terakhir, saya harus perhatikan bahwa solusi ini hanya mengimplementasikan konstruksi shell portabel. Dengan kata lain, ini harus menghasilkan hasil yang identik dalam setiap shell yang mengklaim kompatibilitas POSIX. Walaupun tentu saja akan menjadi omong kosong bagi saya untuk mengklaim setiap sistem yang kompatibel dengan POSIX harus menangani
ruby bundle
arahan, setidaknya perintah shell yang memanggilnya harus berperilaku sama terlepas dari apakah shell panggilannyash
atau tidakdash
. Juga hal di atas akan bekerja seperti yang diharapkan (anggap setidaknya setengah warasshopts
) di keduanyabash
danzsh
.sumber
~/.bashrc
dan menelepon. ~/.bashrc
, lalu ketiga fungsi ini dijalankan. Mungkin perilakunya berbeda dengan lingkungan, jadi saya menambahkan lingkungan saya ke pertanyaan. Selain itu saya tidak mengerti mengapa baris terakhir_guard ; _rspec ; _rake
diperlukan. Saya mencarishift
dan mengajukan deskriptor, sepertinya ini di luar pemahaman saya saat ini.sumber