determinare la shell nello script durante il runtime


22

Per quanto ne so, per determinare la shell corrente che usiamo echo $0nella shell. Piuttosto voglio che il mio script controlli in quale shell è in esecuzione. Quindi, ho provato a stampare $0nello script e restituisce il nome dello script come dovrebbe. Quindi, la mia domanda è come posso trovare in quale shell è in esecuzione il mio script durante il runtime?


quale linguaggio di scripting stai usando? Inoltre, nel caso peggiore, è sempre possibile eseguire il shell out di un comando di sistema per ottenere i risultati "echo $ 0" all'interno dello script.
BriGuy

echo $0non è un'opzione qui, poiché lo script verrà eseguito su molte macchine diverse in cui la prima cosa che dovrò controllare è la shell.
g4ur4v,

Allora, qual è il linguaggio di scripting allora?
BriGuy

@BriGuy: è uno script shell unix.
g4ur4v,

3
Bene, se aggiungi #! /bin/sh -in alto, verrà eseguito sh. Intendi che variante shè?
Stéphane Chazelas,

Risposte:


28

Su Linux puoi usare /proc/PID/exe.

Esempio:

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

3
È un po 'troppo specifico per me (es. Su Debian stampa zsh4 o ksh93). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlinedà invece zsh o ksh. (Sarebbe $ 0 se le shell non lo risolvessero magicamente per dare invece il nome degli script).
frostschutz,

@frostschutz La tua è la risposta migliore, corri per il +500!
Teresa e Junior,

5
Questo soffre del temuto tutto il mondo una malattia delle scatole di Linux . /procè brutto e inestimabile come si arriva.
Jens,

7
@Jens ecco perché ho specificato che questo vale solo per Linux. /procnon è "brutto". /procè spesso una soluzione molto elegante. Inarrestabile sì, ma perché qualcosa non è portabile non lo rende brutto.
Patrick,

3
@Patrick Considero /procbrutto perché i file in esso possono andare e venire per capriccio degli sviluppatori e il contenuto dei file è soggetto a modifiche senza preavviso, causando dolore infinito a causa di bitrot e spostando i formati di file di destinazione.
Jens,

48

Forse non è quello che stai chiedendo, ma questo dovrebbe funzionare in una certa misura per identificare l'interprete che attualmente lo interpreta per alcuni come Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, Ordinaria (posh) conforme alle politiche, Yet Another (yash), rc, akanga, es shells, wish, tclsh, prevedono, perl, python, ruby, php, JavaScript (nodejs, SpiderMonkey shell e JSPL almeno) , 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 } *///'''

Ho pubblicato la versione iniziale di quello script that_interpreter intorno al 2004 su usenet. Sven Mascheck ha uno script (probabilmente più utile per te) chiamato whatshell che si concentra sull'identificazione di shell tipo Bourne. È inoltre possibile trovare una versione fusa dei nostri due script .


3
Questo non può identificare Python 3, solo Python 2. Per risolvere il problema, cambia printin una funzione.
Chris Down

39
Questo è il più grande momento WTF dell'anno finora. +1 per portare la portabilità oltre la sanità mentale.
l0b0

1
Sarebbe bello se riconoscesse la conchiglia.
Konrad Borowski il

2
@xfix, ricordo di aver provato anche prima di aggiungere php e javascript ma non ho trovato una soluzione. La complessità cresce esponenzialmente con il numero di lingue da supportare (poiché tutto ciò che aggiungi deve essere valido (o almeno avere effetti collaterali impercettibili) in tutte le lingue supportate), quindi ora sarebbe ancora più difficile. Non sto dicendo che è impossibile ma ciò significherebbe probabilmente far cadere il supporto per alcune altre lingue.
Stéphane Chazelas,

4
@iconoclast, quindi identifica correttamente bash 3.2.53(1)-releasel'interprete che lo interpreta.
Stéphane Chazelas il

12

Questo è ciò che uso nel mio .profile per controllare varie shell sui sistemi su cui lavoro. Non distingue bene tra ksh88 e ksh93, ma non mi ha mai deluso.

Si noti che non richiede una singola forcella o tubo.

# 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
Nota che solo versioni molto recenti di ksh93hanno $KSH_VERSION. Quella variabile proviene pdkshe non è mai arrivata ad AT&T ksh88.
Stéphane Chazelas,

Bene, ecco perché ho il secondo test per FCEDIT.
Jens,

1
Destra. Nota che posh(pdksh con la maggior parte delle funzionalità non POSIX rimosse, quindi probabilmente dovresti chiamarlo "sh") non ha FCEDIT né KSH_VERSION ma ha PS3 (forse non per molto), anche se è improbabile che uno lo abbia come shell di login . Si noti inoltre che il codice sopra riportato non rifletterebbe se si sono basho zshsono in shmodalità di emulazione, il che potrebbe essere un problema se si utilizza $PROFILE_SHELLper decidere se abilitare o meno questa o quella funzionalità. Vedi anche la whatshell di Sven Mascheck per ulteriori informazioni che potresti (o non vuoi) controllare.
Stéphane Chazelas,

6

Potresti provare

ps -o args= -p "$$"

che ti darà il nome del comando associato al pid dello script.


Non funziona quando uso un shebang per quanto ne so. sprunge.us/QeHD
Chris Down

Mi dispiace, @ChrisDown, Flup. Mio male, mi ero erroneamente tradotto cmdin commPOSIXificazione della risposta.
Stéphane Chazelas,

1

Se è lsofdisponibile il comando sul tuo sistema, puoi ottenere il percorso completo dell'eseguibile della shell padre ottenendo il PID padre pse analizzando il risultato di lsof -p $ppid(vedi Come determinare la shell corrente su cui sto lavorando? ).

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

Sul mio sistema questo ritorna /, se uso NR==4ottengo il percorso del genitore shell.
Thor,

Nota che POSIX shha la $PPIDvariabile. Su Linux, puoi usare readlink -f "/proc/$PPID/exe".
Stéphane Chazelas,

1

Al di fuori della terra Linux o in mancanza di accesso al filesystem / proc o equivelent, è possibile utilizzare pstree:

Supponendo che tu abbia il pid di

Su un 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

Su un box Linux:

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

Il formato e lo stile dell'output di pstree differiscono, a seconda del proprio ambiente, ma è possibile applicare l'output ASCII e quindi sed / tr / awk / etc. filtra l'output per ottenere la shell che esegue lo script.

Quindi una versione di output ripulita (funziona per Mac o Linux):

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

Resa in corsa:

./test.sh 
sh

E quando eseguito con una shell diversa:

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

I rendimenti:

./test.sh 
ksh

Non sono richiesti root o filesystem speciali. Nota, il mio filtro presuppone che il nome binario della shell finisca con sh e che non ci siano voci intermedie che finiscono con sh. Suppone anche che non hai chiamato il tuo script "sh" o qualche sfortunato modello grep che cancellerà le informazioni. :) Richiederà un po 'di personalizzazione per il tuo ambiente per garantire un livello superiore di impermeabilità.


-2

Puoi usare il comando:

$ echo $SHELL

per scoprire la shell dall'interno dello script.


18
No. $SHELLè la shell di scelta dell'utente. Inizializzato dalla shell di login dell'utente. Niente a che fare con la shell attualmente in esecuzione.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.