Saya memiliki skrip di mana saya tidak ingin memanggilnya exit
jika bersumber.
Saya berpikir untuk memeriksa apakah $0 == bash
tetapi ini memiliki masalah jika skrip tersebut bersumber dari skrip lain, atau jika pengguna sumbernya dari shell yang berbeda suka ksh
.
Apakah ada cara yang dapat diandalkan untuk mendeteksi jika skrip sedang bersumber?
Jawaban:
Ini tampaknya portabel antara Bash dan Korn:
Baris yang mirip dengan ini atau tugas seperti
pathname="$_"
(dengan tes dan tindakan nanti) harus di baris pertama skrip atau di baris setelah shebang (yang, jika digunakan, harus untuk ksh agar dapat bekerja di bawah sebagian besar keadaan).sumber
BASH_ENV
,$_
di bagian atas skrip akan menjadi perintah terakhir dijalankanBASH_ENV
.$_
merupakan masalah.bash script
(doa melalui executable shell, yang solusi ini salah laporkan sebagai sumbernya ), dan (b) (jauh lebih kecil kemungkinannya)echo bash; . script
(jika$_
kebetulan cocok dengan shell yang mengambil skrip, solusi ini salah melaporkannya sebagai sebuah subkulit ). Hanya variabel khusus spesifik-shell (mis.,$BASH_SOURCE
) Yang memungkinkan solusi yang tangguh (berarti tidak ada solusi yang sesuai dengan POSIX). Hal ini dimungkinkan, meskipun rumit, untuk kerajinan tes lintas shell kuat.Jika versi Bash Anda tahu tentang variabel array BASH_SOURCE, coba sesuatu seperti:
sumber
${BASH_SOURCE[0]}
bukan hanya$BASH_SOURCE
? Dan${0}
vs$0
?BASH_SOURCE
adalah variabel array (lihat manual ) yang menyimpan jejak tumpukan sumber, di mana${BASH_SOURCE[0]}
yang terbaru. Kawat gigi digunakan di sini untuk memberi tahu bash apa yang merupakan bagian dari nama variabel. Mereka tidak diperlukan$0
dalam kasus ini, tetapi mereka juga tidak terluka. ;)$array
, Anda mendapatkan${array[0]}
secara default. Jadi, sekali lagi, apakah ada alasan [...]?Solusi kuat untuk
bash
,ksh
,zsh
, termasuk cross-shell satu, ditambah solusi POSIX-compliant cukup kuat :Nomor versi yang diberikan adalah yang di mana fungsionalitasnya diverifikasi - kemungkinan, solusi ini juga bekerja pada versi yang lebih awal, - umpan balik diterima .
Menggunakan fitur POSIX saja (seperti pada
dash
, yang bertindak sebagai/bin/sh
pada Ubuntu), tidak ada cara yang kuat untuk menentukan apakah skrip sedang bersumber - lihat di bawah untuk perkiraan terbaik .Ikuti satu kalimat - penjelasan di bawah ini; versi cross-shell rumit, tetapi harus bekerja dengan baik:
bash (diverifikasi pada 3.57 dan 4.4.19)
ksh (diverifikasi pada 93u +)
zsh (diverifikasi pada 5.0.5) - pastikan untuk memanggil ini di luar fungsi
lintas-shell (bash, ksh, zsh)
Sesuai dengan POSIX ; bukan satu-liner (pipa tunggal) karena alasan teknis dan tidak sepenuhnya kuat (lihat bawah):
Penjelasan:
pesta
Catatan: Teknik ini diadaptasi dari jawaban pengguna5754163 , karena ternyata lebih kuat dari solusi aslinya,
[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
[1]Bash memungkinkan
return
pernyataan hanya dari fungsi dan, dalam lingkup tingkat atas skrip, hanya jika skrip tersebut bersumber .return
digunakan dalam lingkup tingkat atas dari skrip non-sumber , pesan kesalahan dipancarkan, dan kode keluar diatur ke1
.(return 0 2>/dev/null)
dieksekusireturn
dalam subkulit dan menekan pesan kesalahan; setelah itu kode keluar menunjukkan apakah skrip bersumber (0
) atau tidak (1
), yang digunakan dengan&&
dan||
operator untuk mengatursourced
variabel sesuai.return
dalam lingkup tingkat atas dari skrip bersumber akan keluar dari skrip.0
sebagaireturn
operan; ia mencatat: per bash bantuanreturn [N]
: "Jika N dihilangkan, status pengembalian adalah perintah terakhir." Akibatnya, versi sebelumnya [yang hanya digunakanreturn
, tanpa operan] menghasilkan hasil yang salah jika perintah terakhir pada shell pengguna memiliki nilai balik yang tidak nol.ksh
Variabel khusus
${.sh.file}
agak analog dengan$BASH_SOURCE
; perhatikan bahwa${.sh.file}
menyebabkan kesalahan sintaks di bash, zsh, dan dash, jadi pastikan untuk menjalankannya secara kondisional dalam skrip multi-shell.Tidak seperti dalam bash,
$0
dan${.sh.file}
TIDAK dijamin persis sama dalam kasus non-bersumber, karena$0
mungkin merupakan jalur relatif , sementara${.sh.file}
selalu jalur penuh , jadi$0
harus diselesaikan ke jalur penuh sebelum membandingkan.zsh
$ZSH_EVAL_CONTEXT
berisi informasi tentang konteks evaluasi - sebut ini di luar fungsi. Di dalam skrip bersumber [lingkup tingkat atas],$ZSH_EVAL_CONTEXT
diakhiri dengan:file
.Peringatan: Di dalam substitusi perintah, zsh menambahkan
:cmdsubst
, jadi uji$ZSH_EVAL_CONTEXT
untuk di:file:cmdsubst$
sana.Hanya menggunakan fitur POSIX
Jika Anda ingin membuat asumsi tertentu, Anda dapat membuat dugaan yang masuk akal, tetapi tidak bodoh, apakah skrip Anda dibuat, berdasarkan pada mengetahui nama file biner dari shell yang mungkin mengeksekusi skrip Anda .
Khususnya, ini berarti bahwa pendekatan ini gagal jika skrip Anda bersumber dari skrip lain .
Bagian "Cara menangani doa bersumber" dalam jawaban saya ini membahas kasus tepi yang tidak dapat ditangani dengan fitur POSIX hanya secara rinci.
Ini bergantung pada perilaku standar
$0
, yangzsh
, misalnya, tidak menunjukkan.Dengan demikian, pendekatan teraman adalah menggabungkan metode yang kuat dan spesifik-shell di atas dengan solusi fallback untuk semua shell yang tersisa.
Ujung topi untuk Stéphane Desneux dan jawabannya untuk inspirasi (mengubah ekspresi pernyataan lintas-shell saya menjadi pernyataan
sh
-compatibleif
dan menambahkan handler untuk shell lain).[1] user1902689 menemukan bahwa
[[ $0 != "$BASH_SOURCE" ]]
menghasilkan false positif ketika Anda mengeksekusi skrip yang terletak di$PATH
dengan meneruskan nama file yang hanya kebash
biner; misalnyabash my-script
, karena$0
kemudian hanyamy-script
, sedangkan$BASH_SOURCE
adalah path lengkap . Meskipun Anda biasanya tidak akan menggunakan teknik ini untuk menjalankan skrip di$PATH
- Anda hanya akan memohonnya secara langsung (my-script
) - akan sangat membantu jika dikombinasikan dengan-x
untuk debugging .sumber
Setelah membaca jawaban @ DennisWilliamson, ada beberapa masalah, lihat di bawah:
Seperti pertanyaan ini ksh dan pesta, ada bagian lain dalam jawaban ini mengenai ksh... Lihat di bawah.
Sederhana pesta cara
Mari kita coba (dengan cepat karena pesta itu bisa ;-):
source
Sebagai gantinya saya menggunakan off.
untuk dibaca (seperti.
alias untuksource
):Perhatikan bahwa nomor proses tidak berubah saat proses tetap bersumber :
Mengapa tidak menggunakan
$_ == $0
perbandinganUntuk memastikan banyak kasus, saya mulai menulis skrip yang benar :
Salin ini ke file bernama
testscript
:Sekarang kita bisa menguji:
Tidak apa-apa.
Tidak apa-apa.
Tapi, untuk menguji skrip sebelum menambahkan
-x
tanda:Atau untuk menggunakan variabel yang telah ditentukan:
Ini tidak akan berfungsi lagi.
Memindahkan komentar dari baris 5 ke 6 akan memberikan jawaban yang lebih mudah dibaca:
Lebih keras: ksh sekarang...
Karena saya tidak menggunakan ksh banyak, setelah beberapa membaca di halaman manual, ada mencoba saya:
Salin ini di
testfile.ksh
:Daripada menjalankannya dua kali:
Dan lihat:
Ada beberapa variabel yang diwarisi dalam proses yang bersumber , tetapi tidak ada yang benar-benar terkait ...
Anda bahkan dapat memeriksa yang
$SECONDS
dekat0.000
, tapi itu memastikan hanya kasus yang bersumber secara manual ...Anda bahkan dapat mencoba memeriksa apakah orang tua itu:
Tempatkan ini di Anda
testfile.ksh
:Dari:
atau
ps ho cmd $PPID
, tetapi ini hanya berfungsi untuk satu tingkat subsesi ...Maaf, saya tidak dapat menemukan cara yang dapat diandalkan untuk melakukan itu, di bawah ksh.
sumber
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]
untuk skrip dibaca melalui pipa (cat script | bash
)..
ini bukan alias untuksource
, sebenarnya sebaliknya.source somescript.sh
adalah Bash-isme dan tidak portabel,. somescript.sh
POSIX dan portabel IIRC.The
BASH_SOURCE[]
jawaban (bash-3.0 dan kemudian) tampaknya sederhana, meskipunBASH_SOURCE[]
ini tidak didokumentasikan untuk bekerja di luar tubuh fungsi (yang saat ini terjadi pada pekerjaan, dalam perselisihan dengan halaman manual).Cara yang paling kuat, seperti yang disarankan oleh Wirawan Purwanto, adalah memeriksa
FUNCNAME[1]
dalam suatu fungsi :Kemudian:
Ini setara dengan memeriksa output
caller
, nilai-nilaimain
dansource
membedakan konteks pemanggil. MenggunakanFUNCNAME[]
save yang Anda ambil dan parsingcaller
output. Anda perlu tahu atau menghitung kedalaman panggilan lokal Anda untuk menjadi benar. Kasus-kasus seperti skrip yang bersumber dari dalam fungsi atau skrip lain akan menyebabkan array (tumpukan) menjadi lebih dalam. (FUNCNAME
adalah variabel array bash khusus, harus memiliki indeks berdekatan yang sesuai dengan panggilan stack, asalkan tidak pernahunset
.)(Dalam bash-4.2 dan yang lebih baru Anda bisa menggunakan formulir yang lebih sederhana
${FUNCNAME[-1]}
sebagai ganti untuk item terakhir dalam array. Lebih baik dan disederhanakan berkat komentar Dennis Williamson di bawah ini.)Namun, masalah Anda sebagaimana dinyatakan adalah " Saya memiliki skrip di mana saya tidak ingin itu memanggil 'keluar' jika sedang bersumber ".
bash
Ungkapan umum untuk situasi ini adalah:Jika skrip bersumber maka
return
akan menghentikan skrip bersumber dan kembali ke pemanggil.Jika skrip dieksekusi, maka
return
akan mengembalikan kesalahan (dialihkan), danexit
akan menghentikan skrip seperti biasa. Keduanyareturn
danexit
dapat mengambil kode keluar, jika diperlukan.Sayangnya, ini tidak berfungsi
ksh
(setidaknya tidak dalam versi turunan AT&T yang saya miliki di sini), ini diperlakukanreturn
sebagai setara denganexit
jika dipanggil di luar fungsi atau skrip sumber-sumber.Diperbarui : Apa yang dapat Anda lakukan dalam versi kontemporer
ksh
adalah memeriksa variabel khusus.sh.level
yang diatur ke kedalaman panggilan fungsi. Untuk skrip yang dipanggil ini pada awalnya tidak akan disetel, untuk skrip bersumber titik akan disetel ke 1.Ini tidak sekuat versi bash, Anda harus menjalankan
issourced()
file yang Anda uji dari tingkat atas atau pada kedalaman fungsi yang diketahui.(Anda mungkin juga tertarik dengan kode ini di github yang menggunakan
ksh
fungsi disiplin dan beberapa tipuan perangkap debug untuk meniru bashFUNCNAME
array.)Jawaban kanonik di sini: http://mywiki.wooledge.org/BashFAQ/109 juga menawarkan
$-
sebagai indikator lain (meskipun tidak sempurna) dari status shell.Catatan:
FUNCNAME[]
tetapi selama hanya item terakhir dalam array yang diuji tidak ada ambiguitas.pdksh
. Hal terdekat yang saya temukan hanya berlaku untukpdksh
, di mana setiap sumber skrip membuka file descriptor baru (dimulai dengan 10 untuk skrip asli). Hampir pasti bukan sesuatu yang ingin Anda andalkan ...sumber
${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}
cara mendapatkan item (bawah) terakhir di tumpukan? Kemudian menguji terhadap "main" (meniadakan untuk OP) adalah yang paling dapat diandalkan untuk saya.PROMPT_COMMAND
set, yang muncul sebagai indeks terakhir dariFUNCNAME
array jika saya jalankansource sourcetest.sh
. Pembalik cek (mencarimain
sebagai indeks terakhir) tampaknya lebih kuat:is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }
.FUNCNAME
hanya tersedia dalam fungsi. Menurut tes saya dengandeclare -p FUNCNAME
,bash
berperilaku berbeda. v4.3 memberikan kesalahan di luar fungsi, sedangkan v4.4 memberideclare -a FUNCNAME
. Kedua (!) Kembalimain
untuk${FUNCNAME[0]}
di script utama (jika dijalankan) saat$FUNCNAME
memberikan apa-apa. Dan: Ada begitu banyak skrip di luar sana "ab" menggunakan$BASH_SOURCE
fungsi luar, yang saya ragu ini dapat atau akan diubah.Catatan editor: Solusi jawaban ini bekerja dengan baik, tetapi
bash
hanya -ya. Itu bisa disederhanakan menjadi(return 2>/dev/null)
.TL; DR
Cobalah untuk menjalankan
return
pernyataan. Jika skrip tidak bersumber, itu akan menimbulkan kesalahan. Anda dapat menangkap kesalahan itu dan melanjutkan sesuai kebutuhan.Masukkan ini ke dalam file dan sebut, katakan, test.sh:
Jalankan secara langsung:
Sumbernya:
Bagi saya, ini berfungsi dalam zsh dan bash.
Penjelasan
The
return
pernyataan akan meningkatkan kesalahan jika Anda mencoba untuk melaksanakannya di luar fungsi atau jika script tidak bersumber. Coba ini dari prompt shell:Anda tidak perlu melihat pesan kesalahan itu, sehingga Anda dapat mengarahkan output ke dev / null:
Sekarang periksa kode keluar. 0 berarti OK (tidak ada kesalahan terjadi), 1 berarti terjadi kesalahan:
Anda juga ingin menjalankan
return
pernyataan di dalam sub-shell. Saatreturn
pernyataan itu dijalankan. . . yah. . . kembali. Jika Anda menjalankannya dalam sub-shell, itu akan kembali keluar dari sub-shell itu, bukan kembali dari skrip Anda. Untuk mengeksekusi di sub-shell, bungkus dalam$(...)
:Sekarang, Anda dapat melihat kode keluar dari sub-shell, yang seharusnya 1, karena kesalahan muncul di dalam sub-shell:
sumber
$ readlink $(which sh)
dash
$ . test.sh
This script is sourced.
$ ./test.sh
This script is sourced.
return
harus dilakukan di tingkat atas ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). Thedash
shell memperlakukan seorangreturn
di tingkat atas sebagaiexit
. Kerang lain sukabash
atauzsh
tidak memungkinkanreturn
di tingkat atas, yang merupakan fitur teknik seperti eksploitasi ini.$
sebelum subkulit. Artinya, gunakan(return >/dev/null 2>&1)
bukannya$(return >/dev/null 2>&1)
- tapi kemudian berhenti bekerja di bash.dash
, di mana solusi ini tidak berfungsi, bertindak sepertish
di Ubuntu, misalnya, solusi ini biasanya tidak berfungsish
. Solusinya tidak bekerja untuk saya di Bash 3.2.57 dan 4.4.5 - dengan atau tanpa$
sebelumnya(...)
(meskipun tidak pernah ada alasan bagus untuk itu$
).return
tanpa nilai pengembalian explcit terputus ketikasource
skrip tepat setelah perintah yang keluar buruk. Usulkan pengeditan peningkatan.FWIW, setelah membaca semua jawaban lain, saya datang dengan solusi berikut untuk saya:
Ini berfungsi untuk semua skrip, yang dimulai dengan
#!/bin/bash
tetapi mungkin bersumber dari shell yang berbeda juga untuk mempelajari beberapa informasi (seperti pengaturan) yang disimpan di luarmain
fungsi.Alih-alih 2 baris terakhir Anda dapat menggunakan kode berikut (menurut saya kurang dapat dibaca) untuk tidak mengatur
BASH_SOURCE
shell lain dan memungkinkanset -e
untuk bekerja dimain
:Resep-skrip ini memiliki sifat-sifat berikut:
Jika dijalankan dengan
bash
cara biasa,main
disebut. Harap dicatat bahwa ini tidak termasuk panggilan sepertibash -x script
(di manascript
tidak mengandung jalur), lihat di bawah.Jika bersumber oleh
bash
,main
hanya dipanggil, jika skrip panggilan kebetulan memiliki nama yang sama. (Misalnya, jika sumber itu sendiri atau melaluibash -c 'someotherscript "$@"' main-script args..
manamain-script
harus, apa yangtest
dilihatnya$BASH_SOURCE
).Jika bersumber / dieksekusi / dibaca /
eval
ed oleh shell selainbash
,main
tidak dipanggil (BASH_SOURCE
selalu berbeda dengan$0
).main
tidak dipanggil jikabash
membaca skrip dari stdin, kecuali jika Anda mengatur$0
agar string kosong seperti ini:( exec -a '' /bin/bash ) <script
Jika dievaluasi oleh
bash
denganeval
(eval "`cat script`"
semua kutipan penting! ) Dari dalam beberapa skrip lain, panggilan inimain
. Jikaeval
dijalankan dari commandline secara langsung, ini mirip dengan kasus sebelumnya, di mana skrip dibaca dari stdin. (BASH_SOURCE
kosong, sedangkan$0
biasanya/bin/bash
jika tidak dipaksa untuk sesuatu yang sama sekali berbeda.)Jika
main
tidak dipanggil, ia kembalitrue
($?=0
).Ini tidak bergantung pada perilaku yang tidak terduga (sebelumnya saya menulis tanpa dokumen, tetapi saya tidak menemukan dokumentasi yang tidak dapat Anda
unset
ubah atau ubahBASH_SOURCE
):BASH_SOURCE
adalah bash reserved array . Tetapi membiarkannyaBASH_SOURCE=".$0"
berubah akan membuka kaleng cacing yang sangat berbahaya, jadi harapan saya adalah, bahwa ini tidak akan berpengaruh (kecuali, mungkin, beberapa peringatan buruk muncul di beberapa versi masa depanbash
).BASH_SOURCE
berfungsi di luar fungsi. Namun sebaliknya (hanya berfungsi dalam fungsi) tidak didokumentasikan. Pengamatannya adalah, itu berfungsi (diuji denganbash
v4.3 dan v4.4, sayangnya saya tidak punyabash
v3.x lagi) dan skrip yang terlalu banyak akan rusak, jika$BASH_SOURCE
berhenti bekerja seperti yang diamati. Karena itu harapan saya adalah, ituBASH_SOURCE
tetap seperti untuk versi masa depanbash
juga.( return 0 )
, yang memberi0
jika bersumber dan1
jika tidak bersumber. Ini datang sedikit tidak terduga tidak hanya untuk saya , dan (menurut bacaan di sana) POSIX mengatakan, bahwareturn
dari subkulit adalah perilaku yang tidak terdefinisi (dan direturn
sini jelas dari subkulit). Mungkin fitur ini pada akhirnya mendapatkan penggunaan yang cukup luas sehingga tidak dapat lagi diubah, tetapi AFAICS ada peluang yang jauh lebih tinggi bahwa beberapabash
versi yang tidak disengaja mengubah perilaku pengembalian dalam kasus tersebut.Sayangnya
bash -x script 1 2 3
tidak berjalanmain
. (Bandingkanscript 1 2 3
mana yangscript
tidak memiliki jalur). Berikut ini dapat digunakan sebagai solusi:bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
bash script 1 2 3
tidak berjalanmain
dapat dianggap sebagai fitur.Perhatikan bahwa
( exec -a none script )
panggilanmain
(bash
tidak meneruskannya$0
ke skrip, untuk ini Anda harus menggunakan-c
seperti yang ditunjukkan pada poin terakhir).Jadi, kecuali untuk beberapa kasus sudut,
main
hanya dipanggil, ketika skrip dieksekusi dengan cara biasa. Biasanya ini adalah apa yang Anda inginkan, terutama karena tidak memiliki kode yang rumit dan sulit dipahami.Mengapa saya pikir ini adalah cara umum yang baik untuk menyelesaikan tantangan
Jika Anda memiliki sesuatu, yang dapat bersumber dari beberapa shell, itu harus kompatibel. Namun (baca jawaban lain), karena tidak ada cara portabel (mudah diterapkan) untuk mendeteksi
source
ing, Anda harus mengubah aturan .Dengan memastikan bahwa skrip harus dijalankan
/bin/bash
, Anda melakukan ini dengan tepat.Ini menyelesaikan semua kasus tetapi mengikuti dalam hal ini skrip tidak dapat berjalan secara langsung:
/bin/bash
tidak diinstal atau disfungsional (i. E. dalam lingkungan boot)curl https://example.com/script | $SHELL
bash
cukup baru. Resep ini dilaporkan gagal untuk varian tertentu. Jadi pastikan untuk memeriksa itu berfungsi untuk kasus Anda.)Namun saya tidak bisa memikirkan alasan sebenarnya di mana Anda membutuhkannya dan juga kemampuan untuk sumber skrip yang persis sama secara paralel! Biasanya Anda bisa membungkusnya dengan mengeksekusi
main
dengan tangan. Seperti itu:$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
Catatan
Jawaban ini tidak akan mungkin terjadi tanpa bantuan semua jawaban lain! Bahkan yang salah - yang awalnya membuat saya memposting ini.
Pembaruan: Diedit karena penemuan baru yang ditemukan di https://stackoverflow.com/a/28776166/490291
sumber
/bin/sh
secara efektifbash
dalam mode POSIX, menetapkan untukBASH_SOURCE
memecah skrip Anda. Dalam kerang lainnya (dash
,ksh
,zsh
), memohon naskah Anda dengan melewati sebagai argumen berkas langsung ke executable shell malfungsi (misal,zsh <your-script>
akan membuat naskah Anda keliru berpikir itu bersumber ). (Anda sudah menyebutkan bahwa perpipaan kerusakan kode, di semua shell.). <your-script>
(sumber) tidak bekerja dari semua shell seperti POSIX pada prinsipnya, hanya masuk akal jika skrip ditulis secara eksplisit untuk menggunakan fitur POSIX saja, sehingga untuk mencegah fitur-fitur spesifik pada satu shell dari memecah eksekusi di kulit lain; karena itu menggunakan garis Bash shebang (bukan#!/bin/sh
) membingungkan - setidaknya tanpa komentar yang mencolok. Sebaliknya, jika skrip Anda dimaksudkan untuk dijalankan dari Bash saja (meskipun hanya karena tidak mempertimbangkan fitur apa yang mungkin tidak portabel), lebih baik menolak eksekusi dalam cangkang non-Bash.main
, tetapi ia melakukan ini dalam kasus ini! Dan ketika bersumber dari/bin/sh
, yaitubash --posix
, hal yang sama terjadi dalam kasus ini, dan itu juga salah.Ini berfungsi nanti di dalam skrip dan tidak tergantung pada variabel _:
atau
sumber
Saya akan memberikan jawaban spesifik BASH. Korn shell, maaf. Misalkan nama skrip Anda adalah
include2.sh
; kemudian buat fungsi di dalam yanginclude2.sh
dipanggilam_I_sourced
. Inilah versi demo sayainclude2.sh
:Sekarang coba jalankan dengan banyak cara:
Jadi ini bekerja tanpa kecuali, dan tidak menggunakan
$_
barang rapuh . Trik ini menggunakan fasilitas introspeksi BASH, yaitu variabel bawaanFUNCNAME
danBASH_SOURCE
; lihat dokumentasi mereka di halaman manual bash.Hanya dua peringatan:
1) panggilan ke
am_I_called
harus dilakukan dalam skrip bersumber, tetapi tidak dalam fungsi apa pun, agar tidak${FUNCNAME[1]}
mengembalikan sesuatu yang lain. Ya ... Anda bisa memeriksa${FUNCNAME[2]}
- tetapi Anda hanya membuat hidup Anda lebih sulit.2) fungsi
am_I_called
harus berada di skrip bersumber jika Anda ingin mengetahui apa nama file yang dimasukkan.sumber
Saya ingin menyarankan sedikit koreksi pada jawaban Dennis yang sangat membantu , agar sedikit lebih portabel, saya harap:
karena
[[
tidak dikenali oleh shell yang kompatibel (agak anal retention IMHO) Debian POSIXdash
,. Juga, orang mungkin perlu tanda kutip untuk melindungi terhadap nama file yang mengandung spasi, sekali lagi di shell tersebut.sumber
$_
cukup rapuh. Anda harus memeriksanya sebagai hal pertama yang Anda lakukan dalam skrip. Dan bahkan kemudian, itu tidak dijamin mengandung nama shell Anda (jika bersumber) atau nama skrip (jika dijalankan).Misalnya, jika pengguna telah menetapkan
BASH_ENV
, maka di bagian atas skrip,$_
berisi nama perintah terakhir yang dijalankan dalamBASH_ENV
skrip.Cara terbaik yang saya temukan adalah menggunakan
$0
seperti ini:Sayangnya, cara ini tidak bekerja di luar kotak di zsh karena
functionargzero
opsi melakukan lebih dari yang disarankan namanya, dan diaktifkan secara default.Untuk bekerja di sekitar ini, saya menempatkan
unsetopt functionargzero
di saya.zshenv
.sumber
Saya mengikuti ekspresi kompak mklement0 .
Itu rapi, tetapi saya perhatikan bahwa itu bisa gagal dalam kasus ksh ketika dipanggil sebagai berikut:
(Diperkirakan itu bersumber dan bukan karena mengeksekusi subkulit) Tetapi ekspresi akan berfungsi untuk mendeteksi ini:
Juga, bahkan jika ekspresi itu kompak, sintaksisnya tidak kompatibel dengan semua shell.
Jadi saya mengakhiri dengan kode berikut, yang berfungsi untuk bash, zsh, dash dan ksh
Jangan ragu untuk menambahkan dukungan kerang eksotis :)
sumber
ksh 93+u
,ksh ./myscript.sh
berfungsi dengan baik untuk saya (dengan pernyataan saya) - versi apa yang Anda gunakan?/proc/$$/cmdline
) dandash
hanya berfokus pada (yang juga bertindak sepertish
pada Ubuntu, misalnya). Jika Anda ingin membuat asumsi tertentu, Anda dapat memeriksa$0
untuk tes masuk akal - tetapi tidak lengkap - yang portabel.sh
/dash
juga, sebagai tambahan untuk jawaban saya.Saya tidak berpikir ada cara portabel untuk melakukan ini di ksh dan bash. Dalam bash Anda bisa mendeteksinya menggunakan
caller
output, tapi saya tidak berpikir ada yang setara di ksh.sumber
$0
bekerja dibash
,ksh93
, danpdksh
. Saya tidak perluksh88
menguji.Saya membutuhkan one-liner yang bekerja di [mac, linux] dengan bash.version> = 3 dan tidak ada jawaban yang sesuai dengan tagihan.
sumber
bash
solusi bekerja dengan baik (Anda bisa menyederhanakan untuk$BASH_SOURCE
), tetapiksh
solusi tidak kuat: jika naskah Anda sedang bersumber oleh script lain , Anda akan mendapatkan positif palsu.Langsung ke titik: Anda harus mengevaluasi apakah variabel "$ 0" sama dengan nama Shell Anda.
Seperti ini:
Melalui SHELL :
Melalui SUMBER :
Sangat sulit untuk memiliki 100% cara portabel mendeteksi apakah sebuah skrip bersumber atau tidak.
Mengenai pengalaman saya (7 tahun dengan Shellscripting) , satu-satunya cara yang aman (tidak bergantung pada variabel lingkungan dengan PID dan sebagainya, yang tidak aman karena fakta bahwa itu adalah sesuatu BERBAGAI ), Anda harus:
Kedua opsi tidak dapat diskalakan secara otomatis, tetapi ini adalah cara yang lebih aman.
Sebagai contoh:
ketika Anda sumber skrip melalui sesi SSH, nilai yang dikembalikan oleh variabel "$ 0" (saat menggunakan sumber ), adalah -bash .
ATAU
sumber
/bin/bash -c '. ./check_source.sh'
memberiThe script WAS NOT sourced.
. Bug yang sama:ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'
->The script WAS NOT sourced.
Saya akhirnya memeriksa
[[ $_ == "$(type -p "$0")" ]]
Saat digunakan
curl ... | bash -s -- ARGS
untuk menjalankan skrip jarak jauh saat dalam keadaan on-the-fly, $ 0 hanya akan menjadibash
normal/bin/bash
ketika menjalankan file skrip yang sebenarnya, jadi saya menggunakantype -p "$0"
untuk menunjukkan path lengkap bash.uji:
sumber
Ini adalah spin-off dari beberapa jawaban lain, mengenai dukungan cross shell "universal". Ini diakui sangat mirip dengan https://stackoverflow.com/a/2942183/3220983 khususnya, meskipun sedikit berbeda. Kelemahannya adalah skrip klien harus menghargai cara menggunakannya (yaitu dengan mengekspor variabel terlebih dahulu). Kekuatannya adalah ini sederhana dan harus bekerja "di mana saja". Berikut template untuk kesenangan cut & paste Anda:
Catatan: Saya menggunakan
export
memastikan mekanisme ini dapat diperluas menjadi sub proses.sumber