Bagaimana saya * andal * dan * cukup * mendapatkan nama juru bahasa shell saat ini?

9

Saya mencari cara yang sederhana dan dapat diandalkan untuk mendapatkan nama shell saat ini dari dalam skrip atau file sumber ( bukan dari baris perintah). Saya diharapkan hanya melakukannya $(basename "$SHELL")tetapi jika shell login saya zshdan saya memiliki kode berikut di some_script.sh

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

dan saya menjalankannya dengan bash some_script.sh, itu masih daftar zshbukannya bashpenerjemah yang digunakan /bin/bash. Saya tidak yakin mengapa perancang shell memilih untuk menampilkan shell default alih-alih shell saat ini, tapi sepertinya itulah yang kami lakukan.

Ada beberapa pertanyaan lain yang agak mirip (di sini dan di sini ), tetapi jawaban mereka gagal dalam banyak hal.

  1. Mereka sering menganggap pengguna sedang mencoba mencari tahu shell interaktif apa yang saat ini mereka gunakan, tetapi itu gila. Aku tahu apa shell saya perintah mengetik ke - Saya perlu kode yang dapat dijalankan di mana saja untuk dapat menentukan apa shell itu menggunakan.
  2. Mereka sering memberikan beberapa hal berbeda untuk dicoba - lagi seolah-olah Anda berada di baris perintah dan hanya bisa bermain-main sampai Anda mengetahui Anda menggunakan bash - tetapi saya membutuhkan satu hal yang dapat diandalkan dalam semua konteks.
  3. Mereka sering memberikan hal-hal yang sama sekali tidak berguna dari skrip, seperti echo $0. Itu gagal seperti yang ditunjukkan dalam skrip di atas. (Ya itu bekerja di baris perintah shell interaktif, tapi mengapa Anda tidak pernah tahu shell apa yang Anda gunakan?)
  4. Mereka kadang-kadang memberikan perintah yang (dalam tes terbatas saya) termasuk informasi yang benar, seperti ps -p $$, tetapi tidak memiliki platform silang, sed-awk kompatibel lintas-shell perintah untuk pipa melalui untuk mendapatkan hanya nama shell dan mengabaikan informasi lain yang datang bersama untuk perjalanan.
  5. Mereka termasuk hal-hal yang hanya akan bekerja pada beberapa cangkang, seperti $BASH_VERSIONdan $ZSH_VERSION. Saya ingin mendukung banyak kerang mungkin, seperti fish, csh, tcsh.

Bagaimana cara andal dan akurat mendeteksi setiap saat shell? Saya mencari sesuatu yang akan bekerja di seluruh platform, dalam skrip, dan sebanyak mungkin kerang dan masuk akal 1 .

UPDATE : Ketika saya memposting pertanyaan ini saya berharap bahwa ada beberapa fasilitas dibangun ke shell untuk memberi kami info ini, yang tampaknya tidak demikian. Karena sekarang tampaknya tak terhindarkan untuk mengandalkan sesuatu di luar cangkang, saya harus membuat lebih eksplisit bahwa saya memintasolusi lintas platform (yang, meskipun tersirat oleh keberatan saya terhadap jawaban lain di atas, mungkin mudah terlewatkan jika Anda tidak membaca pertanyaan dengan cermat).

Pembaruan 2 Jika ada yang masih percaya ini adalah duplikat dari pertanyaan khusus Linux karena jawaban Stéphane bukan hanya untuk Linux, berikut adalah perbedaan antara apa yang saya minta dan apa yang dia sediakan. (Perhatikan bahwa apa yang ditulisnya cerdik, dan saya tidak mengetuknya, tetapi itu tidak menyelesaikan masalah saya.)

  1. Saya mencari sesuatu yang sederhana dan handal , yang
  2. dapat ditambahkan ke skrip atau definisi fungsi apa pun (yang akan bersumber dari .zshrcatau .bash_profileatau apa pun) ke cabang.
  3. Anda tidak dapat menggunakan skripnya sebagai utilitas luar yang meneruskan nama juru bahasa ke skrip panggilan / fungsi, karena itu akan selalu ditafsirkan oleh penerjemah default dan mengembalikannya. Ini membuatnya sulit atau tidak mungkin digunakan untuk tujuan saya. Jika memungkinkan, itu masih sangat sangat sulit, dan solusi untuk membuatnya berfungsi tidak diberikan dalam jawabannya. Karena itu dia tidak menjawab pertanyaan saya, oleh karena itu itu bukan duplikat.

Jika Anda ingin melihat sesuatu yang akan bekerja, lihatlah ShellDetective di GitHub . Itu mungkin membuatnya lebih mudah untuk melihat perbedaan antara apa yang sudah ada pada SE, dan apa yang dicari oleh pertanyaan ini (dan sebenarnya ditulis untuk memenuhi kebutuhan pertanyaan ini, yang tidak terpenuhi di tempat lain).


(PS jika Anda tidak percaya ada use case untuk ini, bayangkan fungsi yang bersumber ke .zshrc,, .bash_profileatau .profiletergantung pada server apa yang sedang digunakan dan apa shell yang telah tersedia. Mereka bersumber sehingga mereka tidak memiliki garis shebang. Mereka paling berguna jika mereka dapat bekerja di shell apa pun, tetapi kadang-kadang mereka harus tahu shell mana yang mereka miliki untuk mengetahui bagaimana harus bersikap.)


1 Saya tidak peduli dengan "kerang" yang bukan kerang dalam arti kata tradisional. Saya tidak peduli dengan beberapa shell yang ditulis oleh seorang pria untuk dirinya sendiri. Saya hanya peduli dengan shell nyata yang benar-benar terjadi di alam liar, dan yang seseorang mungkin harus menggunakan ketika masuk ke beberapa server di mana mereka tidak bisa hanya menginstal shell apa pun yang mereka inginkan.

iconoclast
sumber
Untuk membaca sepenuhnya diskusi di luar topik tentang apakah Python adalah shell: lihat di sini
iconoclast
2
Mengutip Gilles ‘s jawaban untuk pertanyaan Apa perbedaan yang tepat antara 'terminal', sebuah 'shell', sebuah 'tty' dan 'konsol'?   - " Shell adalah antarmuka utama yang dilihat pengguna saat mereka masuk, yang tujuan utamanya adalah memulai program lain." IMO, itu cukup untuk menghilangkan Perl dan Python dari kategori "shell".
G-Man Mengatakan 'Reinstate Monica'
Terkait samar-samar: Skrip kompatibilitas: Hemat $? untuk digunakan nanti . Pendekatan yang disarankan: lakukan akhir-akhir dari perbedaan dalam sintaks dari shell yang berbeda dengan mengatakan sh -c "…", dan kemudian lakukan sebagian besar pekerjaan di sintaks shell POSIX.
G-Man Mengatakan 'Reinstate Monica'
2
Mengapa Anda ingin mendapatkannya ? Beberapa shell, dapat digunakan sebagai shell login, memiliki sintaks yang sangat tidak kompatibel (beberapa shell adalah bahasa mirip Lisp!), Jadi saya tidak mengerti mengapa Anda ingin melakukan itu. Jika Anda menyandikan file yang dapat sumber, Anda perlu membuat kode beberapa varian dan memberi tahu pengguna Anda untuk memilih yang sesuai.
Basile Starynkevitch
1
Pertanyaan kedua saya adalah contoh menyamarkan shell. Apa yang Anda lakukan jika sebuah skrip dieksekusi oleh seorang interpreter yang dapat dieksekusi yang namanya tidak sesuai dengan bahasa yang ditafsirkan oleh interpreter? Jika saya mengambil salinan csh, ganti nama itu fishdan gunakan untuk menjalankan skrip Anda, apakah Anda mau fishatau csh?
Gilles 'SANGAT berhenti menjadi jahat'

Jawaban:

5

Saya mendapatkan hasil yang luar biasa dengan kombinasi ini:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

Pada Tru64 (OSF / 1), output memiliki tanda kurung di sekitar shell. Tack on tr -d '()'untuk menghapusnya.

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Tampaknya berfungsi pada semua shell, pada Solaris 10 dan RHEL 5.7 / 6.4. Tidak menguji distro lain seperti Debian, Ubuntu, atau Mint, tapi saya pikir mereka semua harus bekerja sama; Saya juga tidak tahu apakah FreeBSD akan berfungsi.

masukkan deskripsi gambar di sini

Chris Embry
sumber
3
Tidak berfungsi dalam skrip (sebagai gantinya akan mengembalikan nama skrip)
Qw3ry
2

Jadi saya masih mencoba untuk menyempurnakan ini, tapi saya pikir saya punya ide yang akan berhasil. Seperti yang telah Anda perhatikan, apa yang Anda coba lakukan adalah jika bukan tidak mungkin, sangat sulit dilakukan di semua shell (setiap varian yang Anda tambahkan ke polyglot meningkatkan kerumitan pada tingkat yang lebih besar daripada linear). Anda mungkin dapat melakukannya jika Anda membagi menjadi Bourne (sh, ash, dash, bash, ksh, pdksh, zsh, dll.) dan c style shells (csh, tcsh, ikan, dll.) tetapi variasi antar cshvarian menyajikan berbagai hal menarik tantangan.

Jadi mari kita menipu dan menulis deteksi rutin kita dalam bahasa yang dikenal (bash dengan garis shebang, C, perl, python, apa pun) dan tentukan nama executable dari induknya. kita kemudian dapat menggunakan dua trik untuk mendapatkan informasi kembali ke induk, yaitu nilai pengembalian (s) dan satu kata ditulis ke standar keluar. Pada shell yang diturunkan, kita akan mengembalikan 0 dan menulis nama shell karena semuanya memiliki penanganan backtick yang baik. pada kelompok kerang C kita bisa mulai menambahkan nilai kembali untuk setiap set ketidakcocokan yang perlu kita selesaikan. Nilai pengembalian lebih dari dua ratus akan menunjukkan kesalahan dimulai dengan semuanya tampak baik-baik saja, tetapi saya tidak bisa mengatakan siapa orang tua saya di dua ratus.

Program dalam sepertinya mudah di linux ( /proc/ppid/exesetengah dari pertempuran). Saya cukup yakin ini dapat dilakukan pada bsd dengan opsi ps, tetapi saya tidak memiliki sistem bsd yang berjalan saat ini untuk mengujinya, dan mac saya memerlukan hard drive baru (dan hanya 10,4 saja). Meskipun ini mengurangi masalah sintaksis shell secara signifikan, ia memperkenalkan serangkaian masalah kompatibilitas yang berbeda. Saya masih berpikir itu memiliki kemungkinan.

Hildred
sumber
Ini adalah wawasan yang akhirnya saya setujui — bahwa Anda memerlukan program luar untuk melakukan pekerjaan itu — meskipun saya mengerti ketika membaca komentar G-Man sh -c "...", yang diambilnya dari pertanyaan lain . (Maaf: Saya melewatkan jawaban Anda pada awalnya karena saya tidak melihat kode di dalamnya, dan hanya kembali dan membacanya nanti.)
iconoclast
Ada paradoks dalam pertanyaan @ iconoclast. Dia ingin memanggil itu dari sebuah skrip (perhatikan bahwa beberapa shell tidak mendukung fungsi) yang dapat bersumber dari shell apa pun, sehingga skrip tersebut mungkin sudah polyglot, jadi harus sudah memiliki beberapa dukungan untuk mencari tahu apa yang menafsirkannya. Dalam pengalaman saya, menulis kode polyglot (my which_intepreter adalah contoh ekstrem, tetapi lihat juga di sana untuk satu-satunya shell sangat sulit, dan umumnya tidak sepadan dengan usaha.
Stéphane Chazelas
@ StéphaneChazelas: Saya pikir Anda benar bahwa umumnya tidak sepadan dengan usaha (tentu saja umumnya tidak, dan mungkin selalu tidak) tapi saya tidak siap untuk menyerah ... Saya pikir masalahnya terurai menjadi 2 masalah benar-benar: pertama mendapatkan nama shell, dan 2nd menggunakannya. Keduanya (setidaknya secara praktis) membutuhkan bantuan dari luar untuk menghindari keterbatasan cangkang yang bisa Anda gunakan. Saya telah memecahkan masalah pertama dengan utilitas kecil yang ditulis dalam Crystal, dan ketika saya menemukan waktu saya ingin menyelesaikan yang ke-2 juga. Btw, solusi Anda brilian, tetapi jelas sangat kompleks.
iconoclast
@iconoclas, dengan asumsi Anda dapat mengambil nama shell dengan andal (perhatikan bahwa subtitusi perintah dan sintaks penugasan variabel bervariasi di antara shell), Anda tidak dapat melakukan hal-hal seperti case $shell in sh)...(itu sintaks Bourne-only), atau switch ($shell)(csh-only). test "$shell" = "sh" && do-somethingbekerja di csh/ sh/ rc/ es, tapi tidak fish...
Stéphane Chazelas
ya, saya sudah mengalami ini. Yang terburuk adalah ikan bahkan tidak akan memuat skrip yang memiliki sintaksis tidak suka, jadi Anda tidak bisa hanya mengirim STDERR ke /dev/null. Tapi saya punya solusi, yang akan saya posting setelah semuanya berhasil.
iconoclast
1

coba sesuatu seperti ini

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin
Dany Flores
sumber
Ini gagal di cshdan tcsh.
iconoclast
dengan sedikit modifikasi itu akan bekerja di fish, tapi kemudian hanya dalam fish, sayangnya: ps h -p %self -o args='' | cut -f1 -d' '. Ini menciptakan masalah ayam dan telur ... Anda harus tahu Anda berada di fishdalam untuk mengeksekusi fishversi kode ...
iconoclast
@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- akan bekerja dengan CSH tetapi tidak yang lain
DarkHeart
@DarkHeart: sebenarnya yang gagal untuk, karena tugas variabel tidak masalah pada csh/ tcsh, itu adalah bagian ini: ps -p $$ -o args='' | cut -f1 -d' ', yang kembali -shpada csh, dan -cshdi tcsh.
iconoclast
1

Saya percaya Anda tidak dapat selalu mendapatkan nama shell saat ini , dan saya pikir Anda harus menyadari keterbatasan apa yang mungkin terjadi.

Pada distribusi Linux, sebagian besar pengguna akan memiliki bash login & shell interaktif mereka (karena bashshell default pada kebanyakan distro). Beberapa pengguna akan mengatur shell mereka ke zsh, csh(dan varian) atau ke fish.

(Sebagai komentar dan jawaban lainnya menjelaskan, menemukan andal shell dari bash, zsh, tcsh, fishsudah menantang)

Namun beberapa pengguna yang aneh dapat menetapkan shell login mereka untuk sesuatu yang sama sekali berbeda - juru Lisp, scsh, es, beberapa bahasa scripting à la Python, atau Ocaml, atau Perl, dll ...- dan itu adalah kebebasan mereka untuk melakukannya. Mungkin, beberapa orang mengkode shell mereka sendiri dan menggunakannya secara interaktif. Bahkan jika Anda menemukan shell aneh mereka, Anda tidak akan dapat melakukan sesuatu yang berguna darinya (maka saya yakin Anda tidak harus mencoba untuk mendapatkan nama shell).

Jadi saya kira Anda mengkodekan beberapa file sumber (mungkin menghasilkan satu) untuk mengkonfigurasi beberapa perangkat lunak. Jadi jelaskan saja apa yang Anda lakukan, dan kode untuk kasus umum bash(dan mungkin zsh& tcsh) ....

Basile Starynkevitch
sumber
-2

gunakan di bawah ini dengan penafian risiko sendiri

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}
gwillie
sumber
itu memberi saya garis kosong ... tetapi jika saya mengerti apa yang Anda coba lakukan itu sangat pintar
iconoclast
Seharusnya tidak memberi Anda garis kosong, kecuali -fopsi dalam readlink, beberapa implementasi tidak memiliki saklar itu
gwillie
Saya menggunakan OS X, dan halaman manual mencantumkan -fsebagai opsi yang tersedia, menganggapnya formatsebagai argumen.
iconoclast
Itu mungkin berfungsi dalam beberapa kasus khusus jika $ 0 adalah sebuah skrip (dimulai oleh #! / ...) tetapi itu bukan setup yang biasa. Di sini, sebagian besar $ 0 mengarah ke file ELF.
readlinktidak bekerja pada vm puredarwin saya jadi saya tidak tahu apa yang terjadi di sana.
gwillie