Cara Anda biasanya memasukkan skrip adalah dengan "sumber"
misalnya:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
incl.sh:
echo "The included script"
Output dari mengeksekusi "./main.sh" adalah:
The included script
The main script
... Sekarang, jika Anda mencoba untuk mengeksekusi skrip shell dari lokasi lain, itu tidak dapat menemukan menyertakan kecuali itu di jalur Anda.
Apa cara yang baik untuk memastikan bahwa skrip Anda dapat menemukan skrip sertakan, terutama jika misalnya, skrip harus portabel?
Jawaban:
Saya cenderung membuat skrip saya menjadi relatif satu sama lain. Dengan begitu saya bisa menggunakan dirname:
sumber
which $0
akan bergunaBASH_SOURCE
array, dan bagaimana elemen pertama dalam array ini selalu menunjuk ke sumber saat ini.Saya tahu saya terlambat ke pesta, tetapi ini harus bekerja tidak peduli bagaimana Anda memulai skrip dan menggunakan builtin secara eksklusif:
.
(dot) perintah adalah alias untuksource
,$PWD
adalah Path untuk Direktori Kerja,BASH_SOURCE
adalah variabel array yang anggotanya adalah nama file sumber,${string%substring}
strip pencocokan terpendek $ substring dari belakang $ stringsumber
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
dibutuhkan? Saya dapat menemukan kebutuhan untuk itu jika perintah sedang ditempelkan ke bash prompt untuk dijalankan. Namun, jika berjalan di dalam konteks file skrip, saya tidak dapat melihat kebutuhan untuk itu ...source
s (maksud saya, jika Andasource
skrip yangsource
lain di direktori lain dan seterusnya, masih berfungsi).${BASH_SOURCE[0]}
karena Anda hanya ingin yang terakhir dipanggil? Juga, menggunakanDIR=$(dirname ${BASH_SOURCE[0]})
akan memungkinkan Anda untuk menyingkirkan if-conditionAlternatif untuk:
adalah:
.. keuntungannya adalah tidak memiliki ketergantungan pada dirname, yang bukan perintah bawaan (dan tidak selalu tersedia di emulator)
sumber
basePath=$(dirname $0)
memberi saya nilai kosong saat file skrip yang berisi bersumber.Jika berada di direktori yang sama Anda dapat menggunakan
dirname $0
:sumber
$0
adalah./t.sh
dan dirname mengembalikan.
; 2) setelahcd bin
dikembalikan.
tidak benar.$BASH_SOURCE
tidak lebih baik.source "$(dirname $0)/incl.sh"
bekerja untuk kasus-kasus ituSaya pikir cara terbaik untuk melakukan ini adalah dengan menggunakan cara Chris Boran, TETAPI Anda harus menghitung MY_DIR dengan cara ini:
Mengutip halaman manual untuk tautan baca:
Saya belum pernah menemukan kasus penggunaan di mana
MY_DIR
tidak dihitung dengan benar. Jika Anda mengakses skrip Anda melalui symlink, maka skrip Anda$PATH
akan berfungsi.sumber
$0
secara langsung?/home/you/script.sh
Anda adalah Anda dapatcd /home
dan menjalankan skrip Anda dari sana karena./you/script.sh
Dalam hal inidirname $0
akan kembali./you
dan termasuk skrip lain akan gagalMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Kombinasi jawaban untuk pertanyaan ini memberikan solusi yang paling kuat.
Ini bekerja untuk kami dalam skrip tingkat produksi dengan dukungan dependensi dan struktur direktori:
Metode ini mendukung semua ini:
readlink
)${BASH_SOURCE[0]}
lebih kuat dari$0
sumber
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
jika tautan balik Anda adalah BusyBox v1.01DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
sumber
./incl.sh
memutuskan ke jalur yang sama dengancd
+pwd
. Jadi apa keuntungan dari mengubah direktori?Ini berfungsi bahkan jika skrip bersumber:
sumber
BASH_SOURCE
adalah array jalur, sesuai dengan panggilan-tumpukan. Elemen pertama berhubungan dengan skrip terbaru di stack, yang merupakan skrip yang sedang dieksekusi. Sebenarnya, yang$BASH_SOURCE
disebut sebagai variabel meluas ke elemen pertama secara default, jadi[0]
tidak perlu di sini. Lihat tautan ini untuk detailnya.1. Rapi
Saya menjelajahi hampir setiap saran dan inilah saran yang paling bagus untuk saya:
script_root=$(dirname $(readlink -f $0))
Ia bekerja bahkan ketika skrip disinkronkan ke
$PATH
direktori.Lihat beraksi di sini: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. Yang paling keren
Ini sebenarnya dari jawaban lain di halaman ini, tapi saya juga menambahkannya ke jawaban saya!
2. Yang paling bisa diandalkan
Atau, dalam kasus yang jarang terjadi yang tidak berhasil, berikut adalah pendekatan bukti-peluru:
Anda dapat melihatnya beraksi di
taskrunner
sumber: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerSemoga ini bisa membantu seseorang di luar sana :)
Juga, silakan tinggalkan ini sebagai komentar jika ada yang tidak bekerja untuk Anda dan sebutkan sistem operasi dan emulator Anda. Terima kasih!
sumber
Anda perlu menentukan lokasi skrip lain, tidak ada jalan lain untuk mengatasinya. Saya akan merekomendasikan variabel yang dapat dikonfigurasi di bagian atas skrip Anda:
Sebagai alternatif, Anda dapat mendesak agar pengguna mempertahankan variabel lingkungan yang menunjukkan di mana rumah program Anda berada, seperti PROG_HOME atau semacamnya. Ini dapat disediakan untuk pengguna secara otomatis dengan membuat skrip dengan informasi itu di /etc/profile.d/, yang akan bersumber setiap kali pengguna login.
sumber
Saya sarankan Anda membuat skrip setenv yang tujuan utamanya adalah untuk menyediakan lokasi untuk berbagai komponen di seluruh sistem Anda.
Semua skrip lain kemudian akan sumber skrip ini sehingga semua lokasi umum di semua skrip menggunakan skrip setenv.
Ini sangat berguna saat menjalankan cronjobs. Anda mendapatkan lingkungan minimal saat menjalankan cron, tetapi jika Anda membuat semua skrip cron terlebih dahulu memasukkan skrip setenv maka Anda dapat mengontrol dan menyinkronkan lingkungan yang Anda ingin jalankan cronjobs.
Kami menggunakan teknik seperti itu pada monyet bangun kami yang digunakan untuk integrasi berkesinambungan di proyek sekitar 2.000 kSLOC.
sumber
Balasan Steve jelas merupakan teknik yang benar tetapi harus dire-refored agar variabel installpath Anda berada dalam skrip lingkungan terpisah tempat semua deklarasi tersebut dibuat.
Maka semua skrip sumber skrip itu dan harus menginstal perubahan, Anda hanya perlu mengubahnya di satu lokasi. Membuat lebih banyak hal, eh, futureproof. Ya Tuhan, aku benci kata itu! (-:
BTW Anda harus benar-benar merujuk ke variabel menggunakan $ {installpath} saat menggunakannya dengan cara yang ditunjukkan dalam contoh Anda:
Jika kawat gigi ditinggalkan, beberapa cangkang akan mencoba dan memperluas variabel "installpath / incl.sh"!
sumber
Shell Script Loader adalah solusi saya untuk ini.
Ini menyediakan fungsi bernama include () yang dapat dipanggil berkali-kali dalam banyak skrip untuk merujuk skrip tunggal tetapi hanya akan memuat skrip sekali. Fungsi dapat menerima jalur lengkap atau jalur sebagian (skrip dicari dalam jalur pencarian). Fungsi serupa bernama load () juga disediakan yang akan memuat skrip tanpa syarat.
Ini berfungsi untuk bash , ksh , pd ksh dan zsh dengan skrip yang dioptimalkan untuk masing-masingnya; dan cangkang lain yang secara umum kompatibel dengan sh asli seperti abu , tanda hubung , pusaka sh , dll., melalui skrip universal yang secara otomatis mengoptimalkan fungsinya tergantung pada fitur yang disediakan cangkang.
[Fowarded example]
start.sh
Ini adalah skrip starter opsional. Menempatkan metode startup di sini hanya kenyamanan dan dapat ditempatkan di skrip utama sebagai gantinya. Skrip ini juga tidak diperlukan jika skrip ingin dikompilasi.
main.sh
Abu
b.sh
keluaran:
Yang terbaik adalah skrip berdasarkan itu juga dapat dikompilasi untuk membentuk satu skrip dengan kompiler yang tersedia.
Berikut adalah proyek yang menggunakannya: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Itu dapat berjalan dengan mudah dengan atau tanpa mengkompilasi skrip. Kompilasi untuk menghasilkan satu skrip juga dapat terjadi, dan sangat membantu selama instalasi.
Saya juga membuat prototipe sederhana untuk pihak konservatif yang mungkin ingin memiliki gagasan singkat tentang cara kerja script implementasi: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . Ini kecil dan siapa saja bisa memasukkan kode dalam skrip utama mereka jika mereka mau jika kode mereka dimaksudkan untuk dijalankan dengan Bash 4.0 atau lebih baru, dan itu juga tidak digunakan
eval
.sumber
eval
kode ed untuk memuat dependensi. Aduheval
blok di dekat bagian bawah selalu dijalankan. Jadi apakah itu dibutuhkan atau tidak, itu pasti menggunakaneval
.eval
murni jahat, dan tidak tahu bagaimana memanfaatkannya dengan baik.eval
itu jahat.Secara pribadi letakkan semua perpustakaan di
lib
folder dan gunakanimport
fungsi untuk memuatnya.struktur folder
script.sh
isiPerhatikan bahwa fungsi impor ini harus di awal skrip Anda dan kemudian Anda dapat dengan mudah mengimpor perpustakaan Anda seperti ini:
Tambahkan satu baris di bagian atas setiap perpustakaan (mis. Utils.sh):
Sekarang Anda memiliki akses ke fungsi di dalam
utils.sh
danrequirements.sh
dariscript.sh
TODO: Tulis tautan untuk membuat satu
sh
filesumber
Menggunakan sumber atau $ 0 tidak akan memberi Anda jalan asli skrip Anda. Anda dapat menggunakan id proses skrip untuk mengambil jalur aslinya
Saya menggunakan skrip ini dan selalu membantu saya dengan baik :)
sumber
Saya meletakkan semua skrip startup saya di direktori .bashrc.d. Ini adalah teknik umum di tempat-tempat seperti /etc/profile.d, dll.
Masalah dengan solusi menggunakan globbing ...
... adalah Anda mungkin memiliki daftar file yang "terlalu panjang". Suatu pendekatan seperti ...
... berjalan tetapi tidak mengubah lingkungan seperti yang diinginkan.
sumber
Tentu saja, untuk masing-masing mereka sendiri, tetapi saya pikir blok di bawah ini cukup solid. Saya percaya ini melibatkan cara "terbaik" untuk menemukan direktori, dan cara "terbaik" untuk memanggil skrip bash lain:
Jadi ini mungkin cara "terbaik" untuk memasukkan skrip lain. Ini didasarkan pada jawaban "terbaik" lain yang memberi tahu skrip bash tempat disimpannya
sumber
Ini harus bekerja dengan andal:
sumber
kita hanya perlu mencari tahu folder tempat incl.sh dan main.sh disimpan; ubah main.sh Anda dengan ini:
main.sh
sumber
echo
, dan penggunaan yang salah darised
'sg
pilihan. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
dan kemudiansource "${SCRIPT_DIR}incl.sh"
Menurut
man hier
tempat yang cocok untuk skrip termasuk adalah/usr/local/lib/
Secara pribadi saya lebih suka
/usr/local/lib/bash/includes
untuk menyertakan. Ada lib -helper lib untuk menyertakan lib dengan cara itu:sumber
Anda juga bisa menggunakan:
sumber