tentukan shell dalam skrip saat runtime

22

Sesuai pengetahuan saya, untuk menentukan shell saat ini kami gunakan echo $0dalam shell. Sebaliknya saya ingin skrip saya memeriksa shell mana yang sedang berjalan. Jadi, saya mencoba mencetak $0skrip dan mengembalikan nama skrip sebagaimana mestinya. Jadi, pertanyaan saya adalah bagaimana saya bisa menemukan di mana shell saya menjalankan skrip saat runtime?

g4ur4v
sumber
bahasa skrip apa yang Anda gunakan? Selain itu, kasus yang lebih buruk, Anda selalu dapat mengeluarkan perintah sistem untuk mendapatkan hasil "echo $ 0" di dalam skrip.
BriGuy
echo $0bukan pilihan di sini, karena skrip akan berjalan pada banyak mesin yang berbeda di mana hal pertama yang perlu saya periksa adalah shell.
g4ur4v
Jadi apa bahasa scripting itu?
BriGuy
@ BriGuy: Ini skrip unix shell.
g4ur4v
3
Nah, jika Anda menambahkan #! /bin/sh -di bagian atas, itu akan berjalan sh. Maksud Anda varian apa shitu?
Stéphane Chazelas

Jawaban:

28

Di linux Anda bisa menggunakan /proc/PID/exe.

Contoh:

# readlink /proc/$$/exe
/bin/zsh
Patrick
sumber
3
Itu agak terlalu spesifik untuk saya (mis. Pada Debian ia mencetak zsh4 atau ksh93). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlinememberikan zsh atau ksh sebagai gantinya. (Itu akan menjadi $ 0 jika shell tidak secara ajaib memperbaiki ini untuk memberikan nama skrip sebagai gantinya).
frostschutz
@frostschutz Milikmu adalah jawaban terbaik, jalankan untuk +500!
Teresa e Junior
5
Ini menderita penyakit kotak Linux yang ditakuti Semua dunia . /procadalah jelek dan tidak dapat diangkut karena mendapat.
Jens
7
@ Jens itu sebabnya saya tentukan ini hanya berlaku untuk Linux. /procbukan 'jelek'. /procseringkali merupakan solusi yang sangat elegan. Unportable ya, tetapi karena ada sesuatu yang unportable tidak membuatnya jelek.
Patrick
3
@ Patrick Saya anggap /procjelek karena file di dalamnya mungkin datang dan pergi atas keinginan pengembang dan isi file cenderung berubah tanpa pemberitahuan, menyebabkan rasa sakit yang tak ada habisnya karena bitrot dan memindahkan format file target.
Jens
48

Mungkin bukan yang Anda minta, tetapi ini harus bekerja sampai taraf tertentu untuk mengidentifikasi penerjemah yang saat ini menafsirkannya untuk beberapa seperti Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, Biasa Sesuai Kebijakan (posh), Yet Another (yash), rc, akanga, es shells, wish, tclsh, mengharapkan, perl, python, ruby, php, JavaScript (setidaknya nodejs, SpiderMonkey shell, dan JSPL) , MS / Wine cmd.exe, command.com (MSDOS, FreeDOS ...).

'echo' +"'[{<?php echo chr(13)?>php <?php echo PHP_VERSION.chr(10);exit;?>}\
@GOTO DOS [exit[set 1 [[set 2 package] names];set 3 Tcl\ [info patchlevel];\
if {[lsearch -exact $1 Expect]>=0} {puts expect\ [$2 require Expect]\ ($3)} \
elseif {[lsearch -exact $1 Tk]>=0} {puts wish\ ($3,\ Tk\ [$2 require Tk])} \
else {puts $3}]]]' >/dev/null ' {\">/dev/null \
">"/dev/null" +"\'";q="#{",1//2,"}";a=+1;q='''=.q,';q=%{\"
'echo' /*>/dev/null
echo ">/dev/null;status=0;@ {status=1};*=(" '$' ");~ $status 1&&{e='"\
"';eval catch $2 ^'&version {eval ''echo <='^ $2 ^'&version''}';exit};e='"\
"';if (eval '{let ''a^~a''} >[2] /dev/null'){e='"\
"';exec echo akanga};eval exec echo rc $2 ^ version;\" > /dev/null
: #;echo possibly pre-Bourne UNIX V1-6 shell;exit
if (! $?version) set version=csh;exec echo $version
:DOS
@CLS
@IF NOT "%DOSEMU_VERSION%"=="" ECHO DOSEMU %DOSEMU_VERSION%
@ECHO %OS% %COMSPEC%
@VER
@GOTO FIN
", unless eval 'printf "perl %vd\n",$^V;exit;'> "/dev/null";eval ': "\'';
=S"';f=false e=exec\ echo n=/dev/null v=SH_VERSION;`(eval "f() { echo :
};f")2>$n` $f||$e Bourne-like shell without function
case `(: ${_z_?1}) 2>&1` in 1) $e ash/BSD sh;;esac;t(){
eval "\${$1$v+:} $f &&exec echo ${2}sh \$$1$v";};t BA ba;t Z z;t PO po;t YA ya
case `(typeset -Z2 b=0;$e $b)2>$n` in 00) (eval ':${.}')2>$n&&eval '
$e ksh93 ${.sh.version}';t K pdk;$e ksh88;;esac;case `(eval '$e ${f#*s}$($e 1
)$((1+1))')2>$n` in e12)$e POSIX shell;;esac;$e Bourne-like shell;: }
print "ruby ",RUBY_VERSION,"\n";exit;' ''';import sys
print("python "+sys.version);z='''*/;
s="";j="JavaScript";if(typeof process=="object"){p=console.log;p(process.title
,process.version)}else{p=print;p((f="function")==(t=typeof version)?"string"==
typeof(v=version())?v:(typeof build!=f?"":s= "SpiderMonkey ")+j+" "+v:(t==
"undefined"?j+"?":version)+"\n");if(s)build()}/*
:FIN } *///'''

Saya memposting versi awal skrip which_interpreter sekitar tahun 2004 di usenet. Sven Mascheck memiliki skrip (mungkin lebih bermanfaat bagi Anda) yang disebut whatshell yang berfokus pada pengidentifikasian kerang mirip Bourne. Anda juga dapat menemukan versi gabungan dari dua skrip kami di sana .

Stéphane Chazelas
sumber
3
Ini tidak dapat mengidentifikasi Python 3, hanya Python 2. Untuk memperbaikinya, ubah printmenjadi fungsi.
Chris Down
39
Ini adalah momen WTF terbesar tahun ini. +1 untuk mengambil portabilitas melewati kewarasan.
l0b0
1
Alangkah baiknya jika akan mengenali cangkang ikan.
Konrad Borowski
2
@xfix, saya ingat pernah mencoba bahkan sebelum menambahkan php dan javascript tetapi tidak bisa menemukan solusi. Kompleksitas tumbuh secara eksponensial dengan jumlah bahasa yang didukung (karena semua yang Anda tambahkan harus valid (atau setidaknya memiliki efek samping yang tidak terlalu mencolok) dalam semua bahasa yang didukung) sehingga akan lebih sulit sekarang. Saya tidak mengatakan itu tidak mungkin tetapi itu mungkin berarti menjatuhkan dukungan untuk beberapa bahasa lain.
Stéphane Chazelas
4
@iconoclast, jadi ia mengidentifikasi dengan benar bash 3.2.53(1)-releasesebagai penerjemah yang menerjemahkannya.
Stéphane Chazelas
12

Ini adalah apa yang saya gunakan di profil saya. Untuk memeriksa berbagai shell pada sistem saya bekerja. Itu tidak membuat perbedaan yang baik antara ksh88 dan ksh93, tetapi tidak pernah mengecewakan saya.

Perhatikan bahwa itu tidak memerlukan garpu atau pipa tunggal.

# Determine what (Bourne compatible) shell we are running under. Put the result
# in $PROFILE_SHELL (not $SHELL) so further code can depend on the shell type.

if test -n "$ZSH_VERSION"; then
  PROFILE_SHELL=zsh
elif test -n "$BASH_VERSION"; then
  PROFILE_SHELL=bash
elif test -n "$KSH_VERSION"; then
  PROFILE_SHELL=ksh
elif test -n "$FCEDIT"; then
  PROFILE_SHELL=ksh
elif test -n "$PS3"; then
  PROFILE_SHELL=unknown
else
  PROFILE_SHELL=sh
fi
Jens
sumber
1
Perhatikan bahwa hanya versi terbaru yang ksh93dimiliki $KSH_VERSION. Variabel itu berasal pdkshdan tidak pernah berhasil mencapai AT&T ksh88.
Stéphane Chazelas
Benar, itulah sebabnya saya memiliki tes kedua untuk FCEDIT.
Jens
1
Kanan. Perhatikan bahwa posh(pdksh dengan sebagian besar fitur non-POSIX dihapus sehingga Anda mungkin ingin menyebutnya "sh") tidak memiliki FCEDIT atau KSH_VERSION tetapi memiliki PS3 (mungkin tidak lama), meskipun tidak mungkin bagi seseorang untuk memilikinya sebagai shell login . Perhatikan juga bahwa kode di atas tidak mencerminkan apakah ada bashatau zshdalam shmode emulasi, yang mungkin menjadi masalah jika Anda menggunakan $PROFILE_SHELLuntuk memutuskan apakah akan mengaktifkan fitur ini atau itu. Lihat juga kulit Sven Mascheck untuk informasi lebih lanjut yang mungkin (atau mungkin tidak) ingin Anda periksa.
Stéphane Chazelas
6

Kamu bisa mencoba

ps -o args= -p "$$"

yang akan memberi Anda nama perintah yang terkait dengan pid skrip.

Flup
sumber
Tidak berfungsi saat menggunakan shebang sejauh yang saya tahu. sprunge.us/QeHD
Chris Down
Maaf, @ChrisDown, Flup. Buruk saya, saya telah salah diterjemahkan cmdke commketika POSIXifying jawabannya.
Stéphane Chazelas
1

Jika ada lsofperintah yang tersedia di sistem Anda, Anda bisa mendapatkan path lengkap dari shell induk yang dapat dieksekusi dengan mendapatkan PID induk melalui psdan mem-parsing ouput lsof -p $ppid(lihat Bagaimana menentukan shell saat ini yang sedang saya kerjakan? ).

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'
mari
sumber
Pada sistem saya ini kembali /, jika saya menggunakan NR==4saya mendapatkan path ke induk shell.
Thor
Perhatikan bahwa POSIX shmemiliki $PPIDvariabel. Aktif Linux, Anda bisa menggunakan readlink -f "/proc/$PPID/exe".
Stéphane Chazelas
1

Di luar tanah Linux atau tidak memiliki akses ke sistem file / proc atau equivelent, Anda dapat menggunakan pstree:

Dengan asumsi Anda memiliki pid

Di Mac:

./test.sh 
16012
-+= 00001 root /sbin/launchd
 \-+= 00245 wingwong /sbin/launchd
   \-+= 04670 wingwong /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal -psn_0_2052597
     \-+= 11816 root login -pf wingwong
       \-+= 11817 wingwong -bash
         \-+= 16012 wingwong ksh ./test.sh
           \-+- 16013 wingwong pstree -p 16012

Di kotak Linux:

./test.sh 
14981
bash(14981)---pstree(14982)

Format dan gaya output dari pstree berbeda, tergantung pada lingkungan Anda, tetapi Anda dapat memaksakan output ASCII dan kemudian sed / tr / awk / etc. filter output untuk mendapatkan shell yang menjalankan skrip.

Jadi versi keluaran yang dibersihkan (berfungsi untuk Mac atau Linux OS):

#!/usr/bin/env sh
pstree  -p $$  | tr ' ()' '\012\012\012' | grep -i "sh$" | grep -v "$0" | tail -1

Hasil yang sedang berjalan:

./test.sh 
sh

Dan ketika dijalankan dengan shell yang berbeda:

#!/usr/bin/env ksh
pstree  -p $$  | tr ' ()' '\012\012\012' | grep -i "sh$" | grep -v "$0" | tail -1

Hasil:

./test.sh 
ksh

Tidak diperlukan root atau sistem file khusus. Catatan, pemfilteran saya mengasumsikan bahwa nama biner shell diakhiri dengan sh dan bahwa tidak ada entri perantara yang diakhiri dengan sh. Juga berasumsi bahwa Anda tidak memberi nama skrip Anda "sh" atau beberapa pola grep yang disayangkan yang akan menghapus informasi. :) Akan memerlukan beberapa penyesuaian untuk lingkungan Anda sendiri untuk memastikan tingkat yang lebih tinggi dari yang sangat mudah.

Wing Tang Wong
sumber
-2

Anda dapat menggunakan perintah:

$ echo $SHELL

untuk mengetahui shell dari dalam skrip.

pradeepchhetri
sumber
18
Tidak. Ini $SHELLadalah shell pilihan pengguna. Diinisialisasi dari shell login pengguna. Tidak ada hubungannya dengan shell yang sedang berjalan.
Stéphane Chazelas