Pelingkupan alias dalam fungsi bash

9

Saya menggunakan skrip (yang saya tidak memiliki akses tulis) yang membuat banyak alias untuk mengatur lingkungan. Saya ingin membuat fungsi bash untuk mengatur lingkungan saya, tetapi tampaknya alias tidak bertahan hidup ke fungsi tubuh.

Inilah contoh minimal:

# aliases.sh
alias fooAlias='echo "this will never work!"'  

.

# .bashrc
function setupLotsOfThings() {
    source aliases.sh
    fooAlias
}

.

Sekarang, jika saya hanya sumber secara aliases.shinteraktif, hal-hal berfungsi seperti yang diharapkan:

[mycomputer]~/ $ source aliases.sh
[mycomputer]~/ $ fooAlias
this will never work!

Namun, jika saya sebaliknya memanggil fungsi yang didefinisikan dalam .bashrc saya, itu tidak mengenali alias setelah sumber definisi:

[mycomputer]~/ $ setupLotsOfThings
-bash: fooAlias: command not found

Apa yang terjadi disini? Apakah ada sesuatu yang saya lewatkan tentang ruang lingkup aliasperintah ketika digunakan dalam suatu fungsi?

Sunting: Saya akan menambahkan beberapa detail di luar contoh minimal untuk menyinari apa yang saya coba capai.

Untuk pekerjaan saya, saya mengembangkan dan menjalankan banyak perangkat lunak pada sebuah cluster dan / atau grid. Saya memiliki beberapa proyek yang memerlukan lingkungan yang benar-benar berbeda, seperti versi gcc yang berbeda, rilis perangkat lunak tertentu, konfigurasi dan PATH data, dan berbagai variabel lingkungan. Administrator menyediakan skrip untuk mengatur berbagai hal, biasanya dengan mendefinisikan fungsi shell atau alias, yang memanggil fungsi lain atau alias atau menjalankan berbagai skrip. Bagi saya, ini kotak hitam.

Saya ingin mengatur berbagai lingkungan saya sendiri dengan satu perintah. Saat ini, saya melakukan sesuatu seperti:

[mycomputer]~/ $ source /some/environment/setup/script.sh
[mycomputer]~/ $ aliasToSetupSomeSoftwareVersion    #this was defined in the above
[mycomputer]~/ $ anotherAliasForOtherSoftware
[mycomputer]~/ $ source /maybe/theres/another/script.sh
[mycomputer]~/ $ runSomeOtherSetup      # this was defined in the new script

Perintah-perintah ini umumnya harus dijalankan secara berurutan. Ide saya pada dasarnya adalah hanya menyalin garis-garis di atas ke dalam blok fungsi, tetapi seperti contoh asli menunjukkan, itu tidak berfungsi. Penanganan alternatif lebih dari diterima!

mengejar
sumber

Jawaban:

10

Solusi alternatif adalah menempelkan perintah-perintah itu ke dalam file teks dan bukan blok fungsi. Sesuatu seperti:

## This is needed to make the sourced aliases available
## within the script.
shopt -s expand_aliases

source /some/environment/setup/script.sh
aliasToSetupSomeSoftwareVersion
anotherAliasForOtherSoftware
source /maybe/theres/another/script.sh
runSomeOtherSetup

Simpan itu di setup1.shmana pun Anda suka. Caranya adalah dengan kemudian sumber file ini, jangan jalankan:

$ source setup1.sh

Itu akan menjalankan alias yang ada di skrip dan juga membuatnya tersedia untuk shell Anda saat ini.

Anda dapat lebih menyederhanakan proses dengan menambahkan ini ke .bashrc:

alias setupLotsOfThings="source setup1.sh"

Sekarang Anda cukup menjalankan setupLotsOfThingsdan mendapatkan perilaku yang Anda inginkan dari fungsi.


Penjelasan

Ada dua masalah di sini. Pertama, alias tidak tersedia untuk fungsi yang dideklarasikan tetapi hanya sekali fungsi itu telah keluar dan kedua alias tidak tersedia dalam skrip. Keduanya dijelaskan di bagian yang sama man bash:

Alias ​​tidak diperluas ketika shell tidak interaktif, kecuali jika opsi shell expand_aliases diatur menggunakan shopt (lihat deskripsi shopt di bawah SHELL BUILTIN COMMANDS di bawah).

Aturan tentang definisi dan penggunaan alias agak membingungkan. Bash selalu membaca setidaknya satu jalur input lengkap sebelum menjalankan salah satu perintah di jalur itu. Alias ​​diperluas ketika perintah dibaca, bukan saat dijalankan. Oleh karena itu, definisi alias muncul pada baris yang sama dengan perintah lain tidak berlaku hingga baris input berikutnya dibaca. Perintah yang mengikuti definisi alias di baris itu tidak terpengaruh oleh alias baru. Perilaku ini juga merupakan masalah ketika fungsi dijalankan. Alias ​​diperluas ketika definisi fungsi dibaca, bukan ketika fungsi dieksekusi, karena definisi fungsi itu sendiri adalah perintah majemuk. Sebagai akibatnya, alias yang didefinisikan dalam suatu fungsi tidak
tersedia sampai setelah fungsi tersebut dieksekusi.
Agar aman, selalu letakkan definisi alias pada baris yang terpisah, dan jangan gunakan alias dalam perintah com-pound.

Lalu, ada perbedaan antara mengeksekusi dan sumber file. Pada dasarnya, menjalankan skrip membuatnya berjalan di shell terpisah sementara sumber itu membuatnya berjalan di shell saat ini. Jadi, sumber setup.shmembuat alias tersedia untuk shell induk sementara mengeksekusi sebagai script tidak.

terdon
sumber
Yah, saya sedang mengerjakan sebuah cluster, dan memiliki beberapa skrip "aliasing" yang tersedia yang membantu pengaturan lingkungan perangkat lunak, dll. Skrip alias mendefinisikan banyak alias berbeda; tujuan saya adalah untuk memanggil lingkungan tertentu dengan sumber alias yang benar, dan kemudian menjalankan (beberapa) alias tersebut. Saya lebih suka melakukan ini dalam satu perintah, daripada harus menjalankan beberapa perintah secara berurutan.
kejar
Dan PS script yang mengatur alias sayangnya sangat bertele-tele dan agak lambat (karena menyentuh banyak file di NFS), jadi saya lebih suka untuk tidak sumber semua hal ini misalnya pada saat login.
kejar
@chase tetapi mereka hanya bersumber ketika Anda menjalankan setupLotsOfThings, mereka hanya tidak tersedia untuk fungsi itu sendiri. Mereka bekerja o shell dari mana Anda memanggil fungsi. Lagi pula, jika fungsi Anda hanya sumber alias mengapa tidak hanya menggunakan alias? Sebagai contoh: alias setupstuff="source aliases.sh".
terdon
benar, tapi saya tidak khawatir tentang ruang lingkup per se . Idealnya saya hanya ingin menggabungkan "sumber hal" dan "jalankan alias" menjadi satu. Mungkin bisa dilakukan dengan 3 fungsi? function sourceStuff () {source ...}; function runStuff () {someAlias; ...}; function setupLotsOfThings () {sourceStuff; runStuff; };
kejar
@ chase ya, saya memikirkan itu tetapi tidak berhasil :). Bisakah Anda memperluas pertanyaan Anda dengan beberapa hal lagi yang perlu Anda lakukan? Fungsi hanya dapat menangani begitu banyak kerumitan, Anda mungkin harus menulis skrip kecil sebagai gantinya.
terdon
7

Sebenarnya, alias Anda tersedia setelah fungsi dimuat! Anda dapat menggunakannya di shell interaktif Anda, atau di Anda .bashrcsetelah menjalankan fungsi.

Batasannya adalah bahwa alias dalam definisi fungsi diperluas ketika definisi fungsi dibaca, bukan ketika fungsi dievaluasi. Ini adalah batasan bash. Jadi ini akan berhasil:

function setupLotsOfThings() {
    source aliases.sh
}
setupLotsOfThings
fooAlias

Tapi bukan ini:

function setupLotsOfThings() {
    source aliases.sh
}
function useTheAliases() {
    fooAlias
}
setupLotsOfThings
useTheAliases

Jika Anda membutuhkan alias yang dapat digunakan di dalam fungsi-fungsi dan dapat didefinisikan setelah fungsi diuraikan, buatlah fungsinya sebagai gantinya. Ingatlah bahwa Anda dapat menggunakan commandbuiltin untuk menjalankan perintah eksternal dari fungsi dengan nama yang sama.

Gilles 'SANGAT berhenti menjadi jahat'
sumber