określanie powłoki w skrypcie podczas działania

22

Zgodnie z moją wiedzą, aby określić bieżącą powłokę, której używamy echo $0w powłoce. Zamiast tego chcę, aby mój skrypt sprawdzał, w której powłoce jest uruchomiony. Więc próbowałem wydrukować $0w skrypcie i zwraca nazwę skryptu tak, jak powinien. Moje pytanie brzmi: w jaki sposób mogę znaleźć powłokę, w której działa mój skrypt podczas działania?

g4ur4v
źródło
jakiego używasz języka skryptowego? Co gorsza, zawsze możesz wydać polecenie systemowe, aby uzyskać wyniki „echo $ 0” w skrypcie.
BriGuy 04.04.13
echo $0nie jest tu opcją, ponieważ skrypt będzie działał na wielu różnych maszynach, gdzie pierwszą rzeczą, którą muszę sprawdzić, jest powłoka.
g4ur4v
Czym więc jest język skryptowy?
BriGuy 04.04.13
@BriGuy: To skrypt powłoki uniksowej.
g4ur4v
3
Cóż, jeśli dodasz #! /bin/sh -u góry, będzie działać sh. Masz na myśli, jaki to wariant sh?
Stéphane Chazelas,

Odpowiedzi:

28

W systemie Linux możesz użyć /proc/PID/exe.

Przykład:

# readlink /proc/$$/exe
/bin/zsh
Patrick
źródło
3
Jest to dla mnie zbyt specyficzne (np. Na Debianie drukuje zsh4 lub ksh93). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlinezamiast tego daje zsh lub ksh. (Byłoby to 0 USD, gdyby powłoki nie naprawiły tego magicznie, aby zamiast tego nadać nazwę skryptom).
frostschutz
@frostschutz Twój jest najlepszą odpowiedzią, biegnij po +500!
Teresa e Junior
5
To cierpi z powodu strasznej choroby pudełkowej Linuksa na całym świecie . /procjest tak brzydka i nieprzenośna, jak to tylko możliwe.
Jens
7
@Jens dlatego podałem, że dotyczy to tylko Linuksa. /procnie jest „brzydki”. /procjest często bardzo eleganckim rozwiązaniem. Nieportowalne tak, ale ponieważ coś jest niesportowalne, nie czyni tego brzydkim.
Patrick
3
@Patrick Uważam za /procbrzydki, ponieważ pliki w nim mogą przychodzić i odchodzić według kaprysu programistów, a zawartość plików może się zmieniać bez uprzedzenia, powodując niekończący się ból z powodu bitrotu i przenoszenia formatów plików docelowych.
Jens
48

Może nie to, o co prosisz, ale powinno to w pewnym stopniu działać, aby zidentyfikować interpretera, który interpretuje go obecnie dla kilku takich jak Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, zgodny z zasadami zwykły (posh), Yet Another (yash), rc, akanga, es shells, wish, tclsh, expect, perl, python, ruby, php, JavaScript (przynajmniej nodejs, powłoka SpiderMonkey i 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 } *///'''

Pierwszą wersję skryptu what_interpreter zamieściłem około 2004 r. Na usenet. Sven Mascheck ma (prawdopodobnie bardziej przydatny) skrypt o nazwie whatshell, który koncentruje się na identyfikowaniu powłok podobnych do Bourne'a. Można również znaleźć wersję scaloną naszych dwóch skryptów tam .

Stéphane Chazelas
źródło
3
Nie można zidentyfikować Python 3, tylko Python 2. Aby to naprawić, zmień printsię na funkcję.
Chris Down,
39
To jak dotąd największy moment WTF w roku. +1 za zabranie przenośności w przeszłości.
l0b0
1
Byłoby miło, gdyby rozpoznał skorupę ryby.
Konrad Borowski
2
@xix, pamiętam, że próbowałem jeszcze przed dodaniem php i javascript, ale nie mogłem wtedy znaleźć rozwiązania. Złożoność rośnie wykładniczo wraz z liczbą obsługiwanych języków (ponieważ wszystko, co dodajesz, musi być poprawne (lub przynajmniej mieć niezauważalne skutki uboczne) we wszystkich obsługiwanych językach), więc teraz byłoby jeszcze trudniej. Nie twierdzę, że to niemożliwe, ale prawdopodobnie oznaczałoby to rezygnację z obsługi niektórych innych języków.
Stéphane Chazelas
4
@iconoclast, więc poprawnie identyfikuje się bash 3.2.53(1)-releasejako interpretujący go tłumacz.
Stéphane Chazelas,
12

Właśnie tego używam w moim .profile do sprawdzania różnych powłok w systemach, na których pracuję. Nie robi wyraźnych różnic między ksh88 i ksh93, ale nigdy mnie nie zawiodło.

Pamiętaj, że nie wymaga pojedynczego widelca ani rury.

# 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
źródło
1
Pamiętaj, że tylko bardzo najnowsze wersje ksh93mają $KSH_VERSION. Ta zmienna pochodzi pdkshi nigdy nie dotarła do AT&T ksh88.
Stéphane Chazelas,
Racja, dlatego mam drugi test FCEDIT.
Jens
1
Dobrze. Zauważ, że posh(pdksh z usuniętą większością funkcji innych niż POSIX, więc prawdopodobnie będziesz chciał nazwać to „sh”) nie ma FCEDIT ani KSH_VERSION, ale ma PS3 (może nie na długo), chociaż jest mało prawdopodobne, aby ktoś miał go jako powłokę logowania . Zauważ też, że powyższy kod nie odzwierciedlałby tego, bashczy zshjest w shtrybie emulacji, co może być problemem, jeśli $PROFILE_SHELLdecydujesz, czy włączyć tę czy inną funkcję. Zobacz także whatshell Sven Mascheck, aby dowiedzieć się więcej, co możesz (lub nie) chcieć sprawdzić.
Stéphane Chazelas 16.04.13
6

Możesz spróbować

ps -o args= -p "$$"

która da ci nazwę polecenia powiązanego z pid skryptu.

Flup
źródło
O ile mi wiadomo, nie działa przy użyciu shebang. sprunge.us/QeHD
Chris Down
Przepraszamy, @ChrisDown, Flup. My bad, I błędnie tłumaczone cmdna commkiedy POSIXifying odpowiedź.
Stéphane Chazelas
1

Jeśli w systemie jest lsofdostępna komenda, możesz uzyskać pełną ścieżkę do wykonywalnej powłoki nadrzędnej, pobierając nadrzędny identyfikator PID psi analizując wynik lsof -p $ppid(zobacz Jak określić bieżącą powłokę, nad którą pracuję? ).

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'
Mari
źródło
W moim systemie zwraca to /, jeśli użyję , NR==4otrzymam ścieżkę do rodzica powłoki.
Thor
Zauważ, że POSIX shmają $PPIDzmienną. Włącz Linux, możesz użyć readlink -f "/proc/$PPID/exe".
Stéphane Chazelas
1

Poza linuksem Linux lub brakiem dostępu do systemu plików / proc lub równorzędnym, możesz skorzystać z pstree:

Zakładając, że masz pid

Na komputerze 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

W systemie Linux:

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

Format i styl wyjścia z pstree różni się w zależności od środowiska, ale możesz wymusić wyjście ASCII, a następnie sed / tr / awk / etc. odfiltruj dane wyjściowe, aby uzyskać powłokę uruchamiającą skrypt.

Tak więc wyczyszczona wersja wyjściowa (działa w systemie Mac lub Linux OS):

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

Wydajność przy uruchomieniu:

./test.sh 
sh

A kiedy działa z inną powłoką:

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

Wydajność:

./test.sh 
ksh

Nie wymaga rootowania ani specjalnego systemu plików. Zauważ, że moje filtrowanie zakłada, że ​​nazwa binarna powłoki kończy się na sh i że nie ma żadnych pośrednich wpisów kończących się na sh. Zakłada również, że nie nazwałeś swojego skryptu „sh” lub jakiegoś niefortunnego wzoru grep, który zatarłby informacje. :) Będzie wymagać dostosowania w twoim środowisku, aby zapewnić wyższy stopień niezawodności.

Wing Tang Wong
źródło
-2

Możesz użyć polecenia:

$ echo $SHELL

aby znaleźć powłokę z poziomu skryptu.

pradeepchhetri
źródło
18
Nie. $SHELLJest powłoką wyboru użytkownika. Zainicjowany z powłoki logowania użytkownika. Nie ma to nic wspólnego z aktualnie działającą powłoką.
Stéphane Chazelas,