หาเชลล์ในสคริปต์ระหว่างรันไทม์


22

ตามความรู้ของฉันเพื่อกำหนดเชลล์ปัจจุบันที่เราใช้echo $0ในเชลล์ ค่อนข้างฉันต้องการสคริปต์ของฉันเพื่อตรวจสอบว่าเปลือกมันทำงานอยู่ ดังนั้นฉันพยายามพิมพ์$0ในสคริปต์และส่งคืนชื่อสคริปต์เท่าที่ควร ดังนั้นคำถามของฉันคือฉันจะหาเชลล์ที่สคริปต์ของฉันทำงานในระหว่างรันไทม์ได้อย่างไร


คุณใช้ภาษาสคริปต์อะไร นอกจากนี้ในกรณีที่แย่กว่านั้นคุณสามารถใช้คำสั่งระบบเพื่อให้ได้ผลลัพธ์ "echo $ 0" ในสคริปต์
BriGuy

echo $0ไม่ใช่ตัวเลือกที่นี่เนื่องจากสคริปต์จะทำงานในหลาย ๆ เครื่องสิ่งแรกที่ฉันจะต้องตรวจสอบคือเชลล์
g4ur4v

แล้วภาษาสคริปต์คืออะไร
BriGuy

@BriGuy: มันเป็นเชลล์สคริปต์ยูนิกซ์
g4ur4v

3
ดีถ้าคุณเพิ่มที่ด้านบนก็จะทำงานใน#! /bin/sh - shคุณหมายถึงอะไรshมันแตกต่างกันอย่างไร
Stéphane Chazelas

คำตอบ:


28

บน Linux /proc/PID/exeคุณสามารถใช้

ตัวอย่าง:

# readlink /proc/$$/exe
/bin/zsh

3
มันค่อนข้างเจาะจงเกินไปสำหรับฉัน (เช่น Debian มันพิมพ์ zsh4 หรือ ksh93) /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlineให้ zsh หรือ ksh แทน (นั่นคือ $ 0 ถ้าเชลล์ไม่ได้แก้ไขสิ่งนี้อย่างน่าอัศจรรย์เพื่อให้ชื่อสคริปต์แทน)
frostschutz

@ Frostschutz ขอแสดงความนับถือคำตอบที่ดีที่สุดสำหรับ +500!
Teresa e Junior

5
สิ่งนี้ได้รับผลกระทบจากโรคกล่อง Linux ทั่วโลกที่น่ากลัว /procเป็นที่น่าเกลียดและไม่สามารถอธิบายได้
เจนส์

7
@Jens นั่นเป็นสาเหตุที่ฉันระบุสิ่งนี้ใช้กับ Linux เท่านั้น /procไม่ใช่ 'น่าเกลียด' /procมักจะเป็นทางออกที่หรูหรามาก ไม่สามารถถอดได้ใช่ แต่เนื่องจากมีบางสิ่งที่ไม่สามารถถอดออกได้ไม่ทำให้น่าเกลียด
แพทริค

3
@ แพทริคฉันคิดว่า/procน่าเกลียดเพราะไฟล์ที่อยู่ในนั้นอาจเข้ามาตามความต้องการของนักพัฒนาและเนื้อหาของไฟล์นั้นมีแนวโน้มที่จะเปลี่ยนแปลงโดยไม่ต้องแจ้งให้ทราบล่วงหน้าทำให้เกิดความเจ็บปวดไม่รู้จบเนื่องจากบิตรอต
Jens

48

อาจไม่ใช่สิ่งที่คุณต้องการ แต่สิ่งนี้ควรใช้ในการระบุล่ามในขณะนี้เพื่อตีความบางอย่างเช่น Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, นโยบายที่สอดคล้องตามมาตรฐาน (posh), Yet Another (yash), rc, akanga, es shells, ปรารถนา, tclsh, คาดหวัง, perl, python, ruby, php, JavaScript (nodejs, SpiderMonkey shell และ 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 } *///'''

ฉันโพสต์เวอร์ชันเริ่มต้นของสคริปต์สคริปต์ซึ่ง__prepreter ประมาณปี 2004 บน usenet Sven Mascheck มีสคริปต์ (อาจมีประโยชน์กับคุณมากกว่า) ที่เรียกว่าwhatshellซึ่งมุ่งเน้นไปที่การระบุเชลล์คล้ายบอร์น นอกจากนี้คุณยังสามารถหารุ่นที่ผสานสองสคริปต์ของเรามี


3
สิ่งนี้ไม่สามารถระบุ Python 3 เพียง Python 2 ในการแก้ไขให้เปลี่ยนprintเป็นฟังก์ชัน
Chris Down

39
นี่เป็นช่วงเวลาที่ยิ่งใหญ่ที่สุดของ WTF ในปีนี้ +1 สำหรับการพกพาสติในอดีต
l0b0

1
มันจะดีถ้ามันจะรู้จักเปลือกหอย
Konrad Borowski

2
@ xfix ฉันจำได้ว่าพยายามก่อนเพิ่ม php และ javascript แต่ไม่พบวิธีแก้ปัญหา ความซับซ้อนเพิ่มขึ้นแบบทวีคูณด้วยจำนวนภาษาที่รองรับ (เนื่องจากทุกสิ่งที่คุณเพิ่มต้องถูกต้อง (หรืออย่างน้อยก็มีผลข้างเคียงที่ไม่สามารถสังเกตเห็นได้) ในทุกภาษาที่รองรับ) ดังนั้นมันจึงยากขึ้นในตอนนี้ ฉันไม่ได้บอกว่ามันเป็นไปไม่ได้ แต่นั่นอาจหมายถึงการทิ้งการสนับสนุนภาษาอื่น ๆ
Stéphane Chazelas

4
@iconoclast ดังนั้นมันจึงระบุอย่างถูกต้องว่าbash 3.2.53(1)-releaseเป็นล่ามล่าม
Stéphane Chazelas

12

นี่คือสิ่งที่ฉันใช้ใน. profile ของฉันเพื่อตรวจสอบเชลล์ต่างๆในระบบที่ฉันใช้งาน มันไม่ได้สร้างความแตกต่างที่ดีระหว่าง ksh88 และ ksh93 แต่มันไม่เคยทำให้ฉันผิดหวัง

โปรดทราบว่ามันไม่จำเป็นต้องใช้ส้อมหรือท่อเดี่ยว

# 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

1
โปรดทราบว่าเฉพาะรุ่นล่าสุดมากมีksh93 $KSH_VERSIONตัวแปรนั้นมาจากpdkshและไม่เคยทำกับ AT&T ksh88
Stéphane Chazelas

FCEDITขวาซึ่งเป็นเหตุผลที่ฉันมีการทดสอบที่สองสำหรับ
เจนส์

1
ขวา. โปรดทราบว่าposh(pdksh ที่มีคุณสมบัติที่ไม่ใช่ POSIX ส่วนใหญ่ถูกลบดังนั้นคุณอาจต้องการที่จะเรียกมันว่า "sh") ไม่มี FCEDIT หรือ KSH_VERSION แต่มี PS3 (อาจจะไม่นาน) แม้ว่าจะไม่มีใครที่จะเป็นเปลือกเข้าสู่ระบบ . นอกจากนี้ยังทราบว่ารหัสข้างต้นจะไม่ได้สะท้อนให้เห็นถึงว่าbashหรือzshอยู่ในshโหมดการจำลองซึ่งอาจจะเป็นปัญหาหากคุณใช้$PROFILE_SHELLในการตัดสินใจหรือไม่ที่จะเปิดใช้งานคุณลักษณะนี้หรือว่า ดูสิ่งที่เชลล์ของ Sven Mascheckเพิ่มเติมคุณอาจต้องการตรวจสอบ (หรืออาจไม่)
Stéphane Chazelas

6

คุณสามารถลอง

ps -o args= -p "$$"

ซึ่งจะให้ชื่อของคำสั่งที่เกี่ยวข้องกับ pid ของสคริปต์


ไม่ทำงานเมื่อใช้ shebang เท่าที่ฉันจะบอกได้ sprunge.us/QeHD
Chris Down

ขออภัย @ChrisDown, Flup ไม่ดีของฉันที่ฉันได้แปลไม่ถูกต้องcmdไปcommเมื่อ POSIXifying คำตอบ
Stéphane Chazelas

1

หากมีlsofคำสั่งอยู่บนระบบของคุณคุณสามารถเรียกใช้พา ธ แบบเต็มของพาเรนต์เชลล์ที่สามารถเรียกทำงานได้โดยรับพาเรนต์ PID ผ่านpsและวิเคราะห์ค่า ouput ของlsof -p $ppid(ดูวิธีการกำหนดเชลล์ปัจจุบันที่ฉันใช้งานอยู่ )

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'

ในระบบของฉันสิ่งนี้จะส่งคืน/ถ้าฉันใช้NR==4ฉันจะได้รับพา ธ ไปยังพาเรนต์ของเชลล์
ธ อร์

โปรดทราบว่า POSIX shs มี$PPIDตัวแปร ในวันที่คุณสามารถใช้Linux readlink -f "/proc/$PPID/exe"
Stéphane Chazelas

1

นอกเหนือจากพื้นที่ Linux หรือขาดการเข้าถึงระบบไฟล์ / proc หรือเทียบเท่าคุณสามารถใช้ pstree:

สมมติว่าคุณมี pid ของ

บน 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

บนกล่อง Linux:

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

รูปแบบและสไตล์ของเอาต์พุตจาก pstree แตกต่างกันไปขึ้นอยู่กับสภาพแวดล้อมของคุณ แต่คุณสามารถบังคับใช้เอาต์พุต ASCII จากนั้นกด sed / tr / awk / etc กรองเอาต์พุตเพื่อรับเชลล์ที่รันสคริปต์

ดังนั้นเวอร์ชันเอาต์พุตที่ล้างแล้ว (ทำงานได้กับ Mac หรือ Linux OS):

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

ผลตอบแทนจากการวิ่ง:

./test.sh 
sh

และเมื่อทำงานกับเชลล์ที่แตกต่างกัน:

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

อัตราผลตอบแทน:

./test.sh 
ksh

ไม่จำเป็นต้องรูทหรือระบบไฟล์พิเศษ หมายเหตุการกรองของฉันสมมติว่าชื่อไบนารีของเชลล์ลงท้ายด้วย sh และไม่มีรายการระหว่างกลางที่ลงท้ายด้วย sh นอกจากนี้สมมติว่าคุณไม่ได้ตั้งชื่อสคริปต์ "sh" หรือรูปแบบ grep ที่โชคร้ายซึ่งจะลบล้างข้อมูล :) จะต้องมีการปรับแต่งบางอย่างสำหรับสภาพแวดล้อมของคุณเองเพื่อให้แน่ใจว่าการป้องกันการปลอมแปลงระดับสูงขึ้น


-2

คุณสามารถใช้คำสั่ง:

$ echo $SHELL

เพื่อหาเชลล์จากภายในสคริปต์


18
ไม่$SHELLเป็นเปลือกของตัวเลือกของผู้ใช้ เริ่มต้นจากเชลล์ล็อกอินของผู้ใช้ ไม่มีอะไรเกี่ยวข้องกับเชลล์ที่รันอยู่ในปัจจุบัน
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.