determinar shell en script durante el tiempo de ejecución

22

Según mi conocimiento, para determinar el shell actual que usamos echo $0en el shell. Más bien quiero que mi script verifique en qué shell se está ejecutando. Entonces, intenté imprimir $0en el script y me devuelve el nombre del script como debería. Entonces, mi pregunta es ¿cómo puedo encontrar en qué shell se ejecuta mi script durante el tiempo de ejecución?

g4ur4v
fuente
¿Qué lenguaje de script estás usando? Además, en el peor de los casos, siempre puede desembolsar un comando del sistema para obtener los resultados "echo $ 0" dentro del script.
BriGuy
echo $0No es una opción aquí, ya que el script se ejecutará en muchas máquinas diferentes, donde lo primero que tendré que verificar es el shell.
g4ur4v
Entonces, ¿cuál es el lenguaje de script?
BriGuy
@BriGuy: es un script de shell de Unix.
g4ur4v
3
Bueno, si agrega #! /bin/sh -en la parte superior, se ejecutará sh. ¿Te refieres a qué variante shes?
Stéphane Chazelas

Respuestas:

28

En Linux puedes usar /proc/PID/exe.

Ejemplo:

# readlink /proc/$$/exe
/bin/zsh
Patricio
fuente
3
Eso es demasiado específico para mí (por ejemplo, en Debian imprime zsh4 o ksh93). /bin/sed -r -e 's/\x0.*//' /proc/$$/cmdlineda zsh o ksh en su lugar. (Eso sería $ 0 si los shells no arreglaran esto mágicamente para dar el nombre de los scripts).
frostschutz
@frostschutz Tuya es la mejor respuesta, ¡corre por el +500!
Teresa e Junior
55
Esto sufre de la temida enfermedad de la caja Linux en todo el mundo . /proces tan feo e inportable como se pone.
Jens
77
@Jens es por eso que especifiqué que esto solo se aplica a Linux. /procno es "feo" /procA menudo es una solución muy elegante. Inportable sí, pero porque algo no es portátil no lo hace feo.
Patrick
3
@Patrick Considero /procfeo porque los archivos que contiene pueden ir y venir a voluntad de los desarrolladores y el contenido de los archivos es propenso a cambiar sin previo aviso, lo que causa un dolor infinito debido a los formatos de archivos bitrot y de destino.
Jens
48

Tal vez no sea lo que está pidiendo, pero esto debería funcionar hasta cierto punto para identificar al intérprete que actualmente lo interpreta para algunos como Thompson (osh), Bourne, Bourne-again (bash), Korn (ksh88, ksh93, pdksh, mksh ), zsh, ordinario que cumple con las políticas (posh), Yet Another (yash), rc, akanga, es shells, wish, tclsh, expect, perl, python, ruby, php, JavaScript (nodejs, SpiderMonkey shell y JSPL al menos) , 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 } *///'''

Publiqué la versión inicial de ese script which_interpreter alrededor de 2004 en usenet. Sven Mascheck tiene un script (probablemente más útil para usted) llamado whatshell que se enfoca en identificar shells tipo Bourne. También puede encontrar una versión fusionada de nuestros dos scripts allí .

Stéphane Chazelas
fuente
3
Esto no puede identificar Python 3, solo Python 2. Para arreglar eso, cambie printpara que sea una función.
Chris Down
39
Este es el mayor momento WTF del año hasta ahora. +1 por llevar la portabilidad al pasado.
l0b0
1
Sería bueno si reconociera la concha de pescado.
Konrad Borowski el
2
@xfix, recuerdo haberlo intentado incluso antes de agregar php y javascript pero no pude encontrar una solución. La complejidad crece exponencialmente con la cantidad de idiomas que se admiten (ya que todo lo que agregue debe ser válido (o al menos tener efectos secundarios imperceptibles) en todos los idiomas admitidos), por lo que ahora sería aún más difícil. No digo que sea imposible, pero eso probablemente significaría dejar de admitir algunos otros idiomas.
Stéphane Chazelas
44
@iconoclast, por lo que se identifica correctamente bash 3.2.53(1)-releasecomo el intérprete que lo interpreta.
Stéphane Chazelas
12

Esto es lo que uso en mi .profile para verificar varios shells en los sistemas en los que trabajo. No hace distinciones finas entre ksh88 y ksh93, pero nunca me ha fallado.

Tenga en cuenta que no requiere una sola horquilla o tubería.

# 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
fuente
1
Tenga en cuenta que solo las versiones muy recientes de ksh93have $KSH_VERSION. Esa variable proviene pdkshy nunca llegó a AT&T ksh88.
Stéphane Chazelas
Bien, por eso tengo la segunda prueba FCEDIT.
Jens
1
Correcto. Tenga en cuenta que posh(pdksh con la mayoría de las funciones que no son POSIX eliminadas, por lo que probablemente quiera llamarlo "sh") no tiene FCEDIT ni KSH_VERSION pero tiene PS3 (quizás no por mucho tiempo), aunque es poco probable que uno lo tenga como un shell de inicio de sesión . También tenga en cuenta que el código anterior no reflejaría si está basho zshno en shmodo de emulación, lo que puede ser un problema si está utilizando $PROFILE_SHELLpara decidir si habilitar o no esta o aquella característica. Vea también el whatshell de Sven Mascheck para obtener más información que puede (o no) que desee consultar.
Stéphane Chazelas
6

Tu podrías intentar

ps -o args= -p "$$"

que le dará el nombre del comando asociado con el pid del script.

Flup
fuente
Por lo que puedo ver, no funciona cuando se usa un shebang. sprunge.us/QeHD
Chris Down
Lo siento, @ChrisDown, Flup. Mi mal, había traducido incorrectamente cmda commPOSIXify la respuesta.
Stéphane Chazelas
1

Si hay un lsofcomando disponible en su sistema, puede obtener la ruta completa del ejecutable del shell principal obteniendo el PID principal psy analizando la salida de lsof -p $ppid(consulte ¿Cómo determinar el shell actual en el que estoy trabajando? ).

#!/bin/sh
ppid="`ps -p "$$" -o ppid=`"
lsof -nP -p "$ppid" | awk 'NR==3 {print $NF; exit}'
mari
fuente
En mi sistema, esto regresa /, si lo uso NR==4, obtengo la ruta al shells parent.
Thor
Tenga en cuenta que los POSIX shtienen la $PPIDvariable. En Linux, puedes usar readlink -f "/proc/$PPID/exe".
Stéphane Chazelas
1

Fuera de la tierra de Linux o sin acceso al sistema de archivos / proc o equivalente, puede utilizar pstree:

Asumiendo que tienes el pid de

En una 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

En una caja de Linux:

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

El formato y el estilo de la salida de pstree difieren, dependiendo de su entorno, pero puede aplicar la salida ASCII y luego sed / tr / awk / etc. filtre la salida para obtener el shell que ejecuta el script.

Entonces, una versión de salida limpia (funciona para Mac o Linux OS):

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

En ejecución rendimientos:

./test.sh 
sh

Y cuando se ejecuta con un shell diferente:

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

Rendimientos:

./test.sh 
ksh

No se requiere root o sistema de archivos especial. Tenga en cuenta que mi filtrado supone que el nombre binario del shell termina con sh y que no hay entradas intermedias que terminen con sh. También supone que no nombró a su script "sh" o algún patrón grep desafortunado que borrará la información. :) Necesitará un poco de personalización para su propio entorno para garantizar un mayor grado de infalibilidad.

Wing Tang Wong
fuente
-2

Puedes usar el comando:

$ echo $SHELL

para descubrir el shell desde el script.

Pradeepchhetri
fuente
18
No. $SHELLes la cáscara de elección del usuario. Inicializado desde el shell de inicio de sesión del usuario. Nada que ver con el shell actualmente en ejecución.
Stéphane Chazelas