Ermitteln der Shell im Skript zur Laufzeit

22

Nach meinem Wissen, um die aktuelle Shell zu bestimmen, die wir echo $0in der Shell verwenden. Ich möchte vielmehr, dass mein Skript überprüft, in welcher Shell es ausgeführt wird. Also habe ich versucht, $0das Skript auszudrucken und es gibt den Namen des Skripts zurück, wie er sollte. Meine Frage ist also, wie kann ich herausfinden, in welcher Shell mein Skript zur Laufzeit ausgeführt wird?

g4ur4v
quelle
Welche Skriptsprache verwenden Sie? Im schlimmsten Fall können Sie auch immer einen Systembefehl ausgeben, um die "echo $ 0" -Ergebnisse innerhalb des Skripts zu erhalten.
BriGuy
echo $0ist hier keine Option, da das Skript auf vielen verschiedenen Computern ausgeführt wird, auf denen ich als erstes die Shell überprüfen muss.
g4ur4v
Was ist dann die Skriptsprache?
BriGuy
@BriGuy: Es ist ein Unix-Shell-Skript.
g4ur4v
3
Nun, wenn Sie oben hinzufügen #! /bin/sh -, wird es ausgeführt sh. Meinen Sie, von welcher Variante shes ist?
Stéphane Chazelas

Antworten:

28

Unter Linux können Sie verwenden /proc/PID/exe.

Beispiel:

# readlink /proc/$$/exe
/bin/zsh
Patrick
quelle
3
Das ist mir ein bisschen zu spezifisch (zB unter Debian gibt es zsh4 oder ksh93 aus). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlinegibt stattdessen zsh oder ksh. (Das wäre $ 0, wenn Shells dies nicht auf magische Weise korrigiert hätten, um stattdessen den Namen des Skripts zu vergeben.)
Frostschutz
@frostschutz Deine ist die beste Antwort, lauf um die +500!
Teresa e Junior
5
Dies leidet unter der gefürchteten Linux-Box- Krankheit All the world . /procist so hässlich und unportabel wie es nur geht.
Jens
7
@Jens deshalb habe ich angegeben, dass dies nur für Linux gilt. /procist nicht 'hässlich'. /procist oft eine sehr elegante Lösung. Nicht portierbar ja, aber weil etwas nicht portierbar ist, macht es es nicht hässlich.
Patrick
3
@Patrick Ich halte das für /prochässlich, da die darin enthaltenen Dateien nach Lust und Laune der Entwickler verschoben werden können und der Inhalt von Dateien sich ohne Vorankündigung ändern kann.
Jens
48

Vielleicht nicht das, wonach Sie fragen, aber dies sollte bis zu einem gewissen Grad funktionieren, um den Interpreter zu identifizieren, der es derzeit für einige interpretiert, wie Thompson (osh), Borowski, Borowski-again (bash), Korn (ksh88, ksh93, pdksh, mksh) ), zsh, Policy-compliant Ordinary (posh), Noch eine (yash), rc, akanga, es shells, wish, tclsh, expect, perl, python, ruby, php, JavaScript (mindestens nodejs, SpiderMonkey-Shell und 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 } *///'''

Ich habe die erste Version des which_interpreter- Skripts von 2004 im Usenet gepostet . Sven Mascheck hat ein (für Sie wahrscheinlich nützlicheres) Skript namens Whatshell , das sich auf die Identifizierung von Bourne-ähnlichen Shells konzentriert. Sie können auch eine fusionierte Version unserer beiden Skripte finden dort .

Stéphane Chazelas
quelle
3
Dies kann nicht Python 3, sondern nur Python 2 identifizieren. Um dies zu beheben, ändern Sie printes in eine Funktion.
Chris Down
39
Dies ist der bisher größte WTF-Moment des Jahres. +1 für Portabilität, die über die Vernunft hinausgeht.
l0b0
1
Es wäre schön, wenn es Fischschalen erkennen würde.
Konrad Borowski
2
@xfix, ich erinnere mich, dass ich es versucht habe, bevor ich PHP und Javascript hinzugefügt habe, aber ich konnte dann keine Lösung finden. Die Komplexität wächst exponentiell mit der Anzahl der zu unterstützenden Sprachen (da alles, was Sie hinzufügen, in allen unterstützten Sprachen gültig sein muss (oder zumindest nicht wahrnehmbare Nebenwirkungen haben muss), wird es jetzt noch schwieriger. Ich sage nicht, dass es unmöglich ist, aber das würde wahrscheinlich bedeuten, dass einige andere Sprachen nicht mehr unterstützt werden.
Stéphane Chazelas
4
@iconoclast, daher wird es korrekt bash 3.2.53(1)-releaseals der Interpreter identifiziert , der es interpretiert.
Stéphane Chazelas
12

Dies ist, was ich in meinem .profile verwende, um nach verschiedenen Shells auf den Systemen zu suchen, auf denen ich arbeite. Es macht keine feinen Unterschiede zwischen ksh88 und ksh93, aber es hat mich nie im Stich gelassen.

Beachten Sie, dass keine einzige Gabel oder Rohr erforderlich ist.

# 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
quelle
1
Beachten Sie, dass nur sehr aktuelle Versionen von ksh93haben $KSH_VERSION. Diese Variable stammt von pdkshund hat es nie zu AT & T ksh88 geschafft.
Stéphane Chazelas
Richtig, deshalb habe ich den zweiten Test für FCEDIT.
Jens
1
Recht. Beachten Sie, dass posh(pdksh, bei dem die meisten Nicht-POSIX-Funktionen entfernt wurden, sodass Sie es wahrscheinlich als "sh" bezeichnen möchten) weder FCEDIT noch KSH_VERSION hat, aber PS3 (möglicherweise nicht lange), obwohl es unwahrscheinlich ist, dass es als Anmeldeshell fungiert . Beachten Sie auch , dass der obige Code würde nicht überlegen , ob bashoder zshin shEmulationsmodus, was ein Problem sein kann , wenn Sie verwenden , $PROFILE_SHELLob oder nicht zu entscheiden , diese oder jene Funktion zu aktivieren. Weitere Informationen finden Sie unter Sven Maschecks Whatshell .
Stéphane Chazelas
6

Du könntest es versuchen

ps -o args= -p "$$"

Hiermit erhalten Sie den Namen des Befehls, der der pid des Skripts zugeordnet ist.

Flup
quelle
Funktioniert nicht, wenn ich einen Shebang benutze, soweit ich das beurteilen kann. sprunge.us/QeHD
Chris Down
Entschuldigung, @ChrisDown, Flup. Meine schlecht, ich hatte falsch übersetzt cmdzu , commwenn POSIXifying die Antwort.
Stéphane Chazelas
1

Wenn der lsofBefehl auf Ihrem System verfügbar ist, können Sie den vollständigen Pfad der ausführbaren Datei der übergeordneten Shell psabrufen, indem Sie die übergeordnete PID über abrufen und die Ausgabe von analysieren lsof -p $ppid(siehe So ermitteln Sie die aktuelle Shell, an der ich arbeite? ).

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'
Mari
quelle
Auf meinem System wird dies zurückgegeben /, wenn ich NR==4den Pfad zum übergeordneten Shell-Element erhalte.
Thor
Beachten Sie, dass POSIX shdie $PPIDVariable haben. Ein Linux, können Sie verwenden readlink -f "/proc/$PPID/exe".
Stéphane Chazelas
1

Außerhalb von Linux oder ohne Zugriff auf das / proc-Dateisystem oder ein gleichwertiges System können Sie pstree verwenden:

Vorausgesetzt, Sie haben die PID von

Auf einem 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

Auf einer Linux-Box:

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

Das Format und der Stil der Ausgabe von pstree unterscheiden sich je nach Ihrer Umgebung, Sie können jedoch die ASCII-Ausgabe erzwingen und dann sed / tr / awk / etc. Filtern Sie die Ausgabe, um die Shell abzurufen, in der das Skript ausgeführt wird.

Also eine aufgeräumte Ausgabeversion (funktioniert für Mac oder Linux OS):

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

Lauferträge:

./test.sh 
sh

Und wenn mit einer anderen Shell ausgeführt:

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

Erträge:

./test.sh 
ksh

Kein root oder spezielles Dateisystem erforderlich. Beachten Sie, dass meine Filterung davon ausgeht, dass der Shell-Binärname mit sh endet und dass es keine Zwischeneinträge gibt, die mit sh enden. Es wird auch davon ausgegangen, dass Sie Ihr Skript nicht "sh" oder ein unglückliches Grep-Muster genannt haben, das Informationen verwischt. :) Erfordert einige Anpassungen für Ihre eigene Umgebung, um ein höheres Maß an Narrensicherheit zu gewährleisten.

Wing Tang Wong
quelle
-2

Sie können den Befehl verwenden:

$ echo $SHELL

um die Shell aus dem Skript herauszufinden.

pradeepchhetri
quelle
18
No. $SHELList die Shell der Wahl des Benutzers. Initialisiert aus der Login-Shell des Benutzers. Mit der aktuell laufenden Shell nichts zu tun.
Stéphane Chazelas