Cómo determinar el shell actual en el que estoy trabajando


634

¿Cómo puedo determinar el shell actual en el que estoy trabajando?

¿ psSería suficiente la salida del comando?

¿Cómo se puede hacer esto en diferentes sabores de Unix?


99
!Probablemente, probar las capacidades particulares (por ejemplo, ¿hace sustitución?) Es más portátil que encontrar el nombre del shell. La costumbre local puede hacer que ejecute algo llamado /bin/shque podría ser ash, dash, bash, etc.
msw

1
@msw: Parece un buen comentario, excepto que me deja preguntándome "¿cómo?".
nobar

Parece que no hay una respuesta simple a esta pregunta. Si no podemos consultar el shell, quizás el mejor enfoque sea especificar siempre el shell. No estoy seguro de que esto siempre sea posible, pero tal vez se logra más fácilmente de lo que la gente generalmente asume.
nobar


@Aniket, no hay tanta ayuda como podría pensar, solo le interesan los procesos de shell interactivos .
Toby Speight

Respuestas:


794
  • Hay tres enfoques para encontrar el nombre del ejecutable del shell actual:

    Tenga en cuenta que los tres enfoques pueden ser engañados si el ejecutable del shell es /bin/sh, pero en realidad es un cambio de nombre bash, por ejemplo (lo que sucede con frecuencia).

    Por lo tanto, su segunda pregunta sobre si la pssalida funcionará se responde con " no siempre ".

    1. echo $0 - imprimirá el nombre del programa ... que en el caso del shell es el shell real.

    2. ps -ef | grep $$ | grep -v grep- esto buscará el ID del proceso actual en la lista de procesos en ejecución. Dado que el proceso actual es el shell, se incluirá.

      Esto no es 100% confiable, ya que es posible que tenga otros procesos cuya pslista incluya el mismo número que el ID de proceso del shell, especialmente si ese ID es un número pequeño (por ejemplo, si el PID del shell es "5", puede encontrar procesos llamados "java5" o "perl5" en la misma grepsalida!). Este es el segundo problema con el enfoque "ps", además de no poder confiar en el nombre del shell.

    3. echo $SHELL- La ruta al shell actual se almacena como la SHELLvariable para cualquier shell. La advertencia para esto es que si inicia un shell explícitamente como un subproceso (por ejemplo, no es su shell de inicio de sesión), obtendrá el valor de su shell de inicio de sesión. Si es una posibilidad, use el enfoque pso $0.


  • Sin embargo, si el ejecutable no coincide con su shell real (por ejemplo, en /bin/shrealidad es bash o ksh), necesita heurística. Aquí hay algunas variables ambientales específicas para varios shells:

    • $version está configurado en tcsh

    • $BASH se establece en bash

    • $shell (minúscula) se establece en el nombre real del shell en csh o tcsh

    • $ZSH_NAME está configurado en zsh

    • ksh tiene $PS3y $PS4establece, mientras que el shell Bourne normal ( sh) solo tiene $PS1y $PS2establece. Esto parece generalmente como el más difícil de distinguir - la única diferencia en todo el conjunto de variables de entorno entre shy kshhemos instalado en Solaris es Boxen $ERRNO, $FCEDIT, $LINENO, $PPID, $PS3, $PS4, $RANDOM, $SECONDS, y $TMOUT.


$ {. sh.version} está configurado en ksh93
fpmurphy

14
ps -p $$como señala Matthew Slattery . Para ksh: echo $KSH_VERSIONo echo ${.sh.version}.
Pausado hasta nuevo aviso.

@Dennish: mi ksh en este momento no tiene configurado KSH_VERSION. y echo ${.sh.version}devuelve "Mala sustitución". Vea mi solución arriba
DVK

3
ps -ef | grep …... Esto no es 100% fiable como ...” Usando una expresión regular simple a través de egrepo grep -efácilmente puede traer la fiabilidad hasta para-todo-intenciones-y-fines 100%: ps -ef | egrep "^\s*\d+\s+$$\s+". Se ^asegura de que estamos comenzando desde el principio de la línea, se \d+come el UID, $$coincide con el PID y el \s*y \s+representa y garantiza el espacio en blanco entre las otras partes.
Slipp D. Thompson

2
@ SlippD.Thompson no funcionaba en GNU / Linux. Pero esto parece funcionar:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

debería funcionar en cualquier lugar donde las soluciones involucren ps -efy lo grephagan (en cualquier variante de Unix que admita las opciones POSIXps ) y no sufrirá los falsos positivos introducidos por grepping para una secuencia de dígitos que puede aparecer en otros lugares.


12
Algunos proyectiles tienen su propia versión incorporada de la pscual puede que no se entienda, -ppor lo que es posible que deba usarla /bin/ps -p $$.
Pausado hasta nuevo aviso.

13
Todos los proyectiles con los que estoy familiarizado entienden, $$excepto los fishque tendrías que usar ps -p %self.
Pausado hasta nuevo aviso.

3
En realidad, no debes confiar en caminos difíciles como /bin/ps. pspodría instalarse fácilmente (en realidad es bastante normal hoy en día) /usr/bin. $(which ps) -p $$Es una mejor manera. Por supuesto, esto no funcionará en peces, y posiblemente en otras conchas. Creo que está (which ps) -p %selfen el pescado.
Aron Cederholm

1
En algunos sistemas mínimos, como un contenedor docker debian-slim, ps podría no estar allí. En ese caso, este enfoque todavía funciona:readlink /proc/$$/exe
mxmlnkn

si tu shes emulado por bash, ps -p te da /usr/bin/bashincluso lo ejecutas comosh
Ding-Yi Chen

45

Tratar

ps -p $$ -oargs=

o

ps -p $$ -ocomm=

44
Este es un buen corto. Me estaba usando a mí mismo ps -o fname --no-headers $$.
Anne van Rossum

Gracias. Encontré esta la mejor opción para usar en un script para proteger comandos específicos de bash test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash. (La siguiente línea en mi script tiene el equivalente para csh.)
craq

1
Descubrí que si hace esto desde una subshell, puede generar líneas adicionales espurias al hacer coincidir el PID del padre y el proceso de shell real. Para esto, uso en -qlugar de -p:SHELL=$(ps -ocomm= -q $$)
Steve

35

Si solo quiere asegurarse de que el usuario invoque un script con Bash:

if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi

1
Este debería ser el más cercano a la cima. Muchas gracias.
Alex Skrypnyk

44
No debería estar más cerca de la cima, porque no responde a la pregunta en absoluto. Si la pregunta sería "Cómo verificar si el script se ejecuta en bash", lo voto.
David Ferenczy Rogožan

2
@DawidFerenczy: sin embargo, esta pregunta es el mejor resultado cuando buscas esa frase. A la larga, creo que es mucho más importante que las respuestas respondan a lo que la gente está buscando en lugar de responder a la pregunta original.
ArtOfWarfare

3
Además, la variable $ BASH se define bajo tcsh si se invoca el tcsh desde bash
18446744073709551615

Esto es muy útil, gracias. Sólo adaptado un poco, poner esto como la segunda línea después #!/bin/bash: if [ ! -n "$BASH" ] ;then exec bash $0; fi. Con esta línea, el script se ejecuta usando bash incluso si se comenzó a usar ksh o sh. Mi caso de uso no necesita argumentos de línea de comando, pero podrían agregarse después $0si es necesario.
joanis

20

Puedes probar:

ps | grep `echo $$` | awk '{ print $4 }'

O:

echo $SHELL

66
¿Cuál es el punto de grep seguido de awk, cuándo /pattern/ { action }lo hará?
Jens

# in zshell alias shell = 'echo $ {SHELL: t}'
SergioAraujo

44
$SHELLLa variable de entorno contiene un shell, que está configurado como predeterminado para un usuario actual. No refleja un shell, que se está ejecutando actualmente. También es mejor usar ps -p $$que grepping $$ debido a falsos positivos.
David Ferenczy Rogožan

El env $ SHELL. La variable apunta al shell 'padre', como se especifica en la especificación POSIX: SHELL Esta variable representará un nombre de ruta del intérprete de lenguaje de comando preferido del usuario. Por lo tanto, el valor de $ SHELL puede no ser el shell actual.
Theo

todo dentro awk,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
go2null

16

$SHELLNo siempre es necesario mostrar el shell actual. Solo refleja el shell predeterminado que se invocará.

Para probar lo anterior, digamos que bashes el shell predeterminado, intente echo $SHELL, y luego en el mismo terminal, ingrese a otro shell ( KornShell (ksh) por ejemplo) e intente $SHELL. Verá el resultado como bash en ambos casos.

Para obtener el nombre del shell actual, use cat /proc/$$/cmdline. Y la ruta al shell ejecutable por readlink /proc/$$/exe.


8
... Siempre que lo hayas hecho /proc.
tripleee

10

ps es el método más confiable. No se garantiza que se establezca la variable de entorno SHELL e incluso si lo es, se puede suplantar fácilmente.


12
+1 $ SHELL es el shell predeterminado para los programas que necesitan generar uno. No necesariamente refleja el shell que se está ejecutando actualmente.
Jim Lewis

8

Tengo un simple truco para encontrar el shell actual. Simplemente escriba una cadena aleatoria (que no es un comando). Fallará y devolverá un error "no encontrado", pero al comienzo de la línea dirá de qué shell se trata:

ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found

3
No es bueno en un guión. echo 'aaaa' > script; chmod +x script; ./scriptda./script.sh: 1: aaaa: not found
delgado

6

Lo siguiente siempre dará el shell real utilizado: obtiene el nombre del ejecutable real y no el nombre del shell (es decir, en ksh93lugar de ksh, etc.). Para /bin/sh, mostrará el shell real utilizado, es decir dash.

ls -l /proc/$$/exe | sed 's%.*/%%'

Sé que hay muchos que dicen que la lssalida nunca debe procesarse, pero ¿cuál es la probabilidad de que tenga un shell que está usando que se nombra con caracteres especiales o se coloca en un directorio con caracteres especiales? Si este sigue siendo el caso, hay muchos otros ejemplos de hacerlo de manera diferente.

Como señaló Toby Speight , esta sería una forma más adecuada y limpia de lograr lo mismo:

basename $(readlink /proc/$$/exe)

3
Esto solo errores en todos los Unices que no proporcionan /proc. No todo el mundo es una caja de Linux.
Jens

55
Y si usted está en Linux, que prefiere basename $(readlink /proc/$$/exe)a ls+ sed+ echo.
Toby Speight

1
Esto le dará el nombre del ejecutable real , no el shell real . Cuando el shell real está vinculado como un applet busybox, digamos ash -> /bin/busybox, esto dará / bin / busybox.
stepse

6

He intentado muchos enfoques diferentes y el mejor para mí es:

ps -p $$

También funciona bajo Cygwin y no puede producir falsos positivos como grepping PID. Con un poco de limpieza, genera solo un nombre ejecutable (en Cygwin con ruta):

ps -p $$ | tail -1 | awk '{print $NF}'

Puede crear una función para no tener que memorizarla:

# Print currently active shell
shell () {
  ps -p $$ | tail -1 | awk '{print $NF}'
}

... y luego simplemente ejecuta shell.

Fue probado bajo Debian y Cygwin.


A partir de mi configuración (Cygwin | Windows 7) su respuesta es la mejor, y ps -p $$ | cola -1 | gawk '{print $ NF}' funciona incluso desde cmd sin $ bash. Tenga en cuenta gawk en lugar de awk, ya que awk solo funciona desde bash.
WebComer

@WebComer Lo siento, no estoy seguro de lo que quieres decir con " funciona incluso desde cmd sin $ bash ". Incluso si tuviera puertos Windows de ps, taily gawk, cmd no se define $$como su PID, por lo que definitivamente no puede funcionar bajo el cmd simple.
David Ferenczy Rogožan

¿Por qué no simplemente ps -p$$ -o comm=? POSIX dice que especificar todos los encabezados vacíos suprime el encabezado por completo. Todavía estamos fallando (como todas las psrespuestas) cuando nos obtiene un script ejecutado directamente (por ejemplo #!/bin/sh).
Toby Speight

5

Mi variante para imprimir el proceso padre:

ps -p $$ | awk '$1 == PP {print $4}' PP=$$

No ejecute aplicaciones innecesarias cuando AWK pueda hacerlo por usted.


2
¿Por qué ejecutar un innecesario awk, cuando ps -p "$$" -o 'comm='puede hacerlo por usted?
Toby Speight

5

Hay muchas formas de averiguar el shell y su versión correspondiente. Aquí hay algunos que me funcionaron.

Sencillo

  1. $> echo $ 0 (Le da el nombre del programa. En mi caso, el resultado fue -bash ).
  2. $> $ SHELL (Esto lo lleva al intérprete de comandos y en el indicador aparece el nombre y la versión del intérprete de comandos. En mi caso bash3.2 $ .)
  3. $> echo $ SHELL (Esto le dará una ruta ejecutable. En mi caso / bin / bash .)
  4. $> $ SHELL --version (Esto proporcionará información completa sobre el software de shell con el tipo de licencia)

Enfoque hackish

$> ******* (Escriba un conjunto de caracteres aleatorios y en la salida obtendrá el nombre del shell. En mi caso -bash: chapter2-a-sample-isomorphic-app: comando no encontrado )


3

Siempre que /bin/shadmita el estándar POSIX y su sistema tenga lsofinstalado el comando, una posible alternativa a lo que lsofpodría ser en este caso pid2path, también puede usar (o adaptar) el siguiente script que imprime las rutas completas:

#!/bin/sh
# cat /usr/local/bin/cursh
set -eu
pid="$$"

set -- sh bash zsh ksh ash dash csh tcsh pdksh mksh fish psh rc scsh bournesh wish Wish login

unset echo env sed ps lsof awk getconf

# getconf _POSIX_VERSION  # reliable test for availability of POSIX system?
PATH="`PATH=/usr/bin:/bin:/usr/sbin:/sbin getconf PATH`"
[ $? -ne 0 ] && { echo "'getconf PATH' failed"; exit 1; }
export PATH

cmd="lsof"
env -i PATH="${PATH}" type "$cmd" 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }

awkstr="`echo "$@" | sed 's/\([^ ]\{1,\}\)/|\/\1/g; s/ /$/g' | sed 's/^|//; s/$/$/'`"

ppid="`env -i PATH="${PATH}" ps -p $pid -o ppid=`"
[ "${ppid}"X = ""X ] && { echo "no ppid found"; exit 1; }

lsofstr="`lsof -p $ppid`" || 
   { printf "%s\n" "lsof failed" "try: sudo lsof -p \`ps -p \$\$ -o ppid=\`"; exit 1; }

printf "%s\n" "${lsofstr}" | 
   LC_ALL=C awk -v var="${awkstr}" '$NF ~ var {print $NF}'

1
esto funciona en peces! pero para mí, falla en bash, debido a la -iopción (-> ignorar entorno) enven la línea donde verifica si lsofestá disponible. falla con: env -i PATH="${PATH}" type lsof->env: ‘type’: No such file or directory
hoijui

2

Si solo desea verificar que está ejecutando (una versión particular de) Bash, la mejor manera de hacerlo es usar la $BASH_VERSINFOvariable de matriz. Como una variable de matriz (solo lectura) no se puede establecer en el entorno, por lo que puede estar seguro de que proviene (si es que lo hace) del shell actual.

Sin embargo, dado que Bash tiene un comportamiento diferente cuando se invoca como sh, también es necesario verificar que la $BASHvariable de entorno termina con /bash.

En un script que escribí que usa nombres de funciones con -(no subrayado), y depende de matrices asociativas (agregadas en Bash 4), tengo la siguiente comprobación de cordura (con un útil mensaje de error del usuario):

case `eval 'echo $BASH@${BASH_VERSINFO[0]}' 2>/dev/null` in
    */bash@[456789])
        # Claims bash version 4+, check for func-names and associative arrays
        if ! eval "declare -A _ARRAY && func-name() { :; }" 2>/dev/null; then
            echo >&2 "bash $BASH_VERSION is not supported (not really bash?)"
            exit 1
        fi
        ;;
    */bash@[123])
        echo >&2 "bash $BASH_VERSION is not supported (version 4+ required)"
        exit 1
        ;;
    *)
        echo >&2 "This script requires BASH (version 4+) - not regular sh"
        echo >&2 "Re-run as \"bash $CMD\" for proper operation"
        exit 1
        ;;
esac

Podría omitir la comprobación funcional paranoica de las funciones en el primer caso, y simplemente asumir que las futuras versiones de Bash serían compatibles.


2

Ninguna de las respuestas funcionó con fishshell (no tiene las variables $$o $0).

Esto funciona para mí (probado en sh, bash, fish, ksh, csh, true, tcsh, y zsh; openSUSE 13.2):

ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'

Este comando genera una cadena como bash. Aquí solo estoy usando ps, taily sed(sin extensiones GNU; intente agregar --posixpara verificarlo). Todos son comandos POSIX estándar. Estoy seguro de que tailse puede eliminar, pero mi sedfu no es lo suficientemente fuerte como para hacer esto.

Me parece que esta solución no es muy portátil ya que no funciona en OS X. :(


Me pongo sed: invalid option -- 'E'en bash 3.2.51 y tcsh 6.15.00
craq

2

Mi solución:

ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1

Esto debería ser portátil en diferentes plataformas y shells. Se utiliza pscomo otras soluciones, pero no se basa en sedo awkfiltra la basura de las tuberías y de pssí misma para que el shell siempre sea la última entrada. De esta manera, no necesitamos confiar en variables PID no portátiles o elegir las líneas y columnas correctas.

He probado en Debian y macOS con Bash, Z shell ( zsh) y fish (que no funciona con la mayoría de estas soluciones sin cambiar la expresión específicamente para fish, porque usa una variable PID diferente).



1

En Mac OS X (y FreeBSD):

ps -p $$ -axco command | sed -n '$p' 

2
Intenté esto mientras lo usaba zsh, y me dio -bash.
user137369

En mi sistema (ahora), probado con bash y dash, este regreso mutt...: -b
F. Hauri

1

No es necesario agrupar el PID de la salida de "ps", ya que puede leer la línea de comando correspondiente para cualquier PID desde la estructura del directorio / proc

echo $(cat /proc/$$/cmdline)

Sin embargo, eso podría no ser mejor que simplemente:

echo $0

Acerca de ejecutar un shell realmente diferente al que indica el nombre, una idea es solicitar la versión del shell utilizando el nombre que obtuvo anteriormente:

<some_shell> --version

sh parece fallar con el código de salida 2 mientras que otros dan algo útil (pero no puedo verificarlo todo porque no los tengo):

$ sh --version
sh: 0: Illegal option --
echo $?
2

1

Esta no es una solución muy limpia, pero hace lo que quieres.

# MUST BE SOURCED..
getshell() {
    local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"

    shells_array=(
    # It is important that the shells are listed in descending order of their name length.
        pdksh
        bash dash mksh
        zsh ksh
        sh
    )

    local suited=false
    for i in ${shells_array[*]}; do
        if ! [ -z `printf $shell | grep $i` ] && ! $suited; then
            shell=$i
            suited=true
        fi
    done

    echo $shell
}
getshell

Ahora puedes usar $(getshell) --version.

Sin embargo, esto funciona solo en shells similares a KornShell (ksh).


1
"Sin embargo, esto funciona solo en proyectiles tipo ksh". ¿Estás diciendo que tengo que verificar el shell antes de ejecutar esto? Hmm ...
jpaugh

@jpaugh, las listas deben ser apoyados por la cáscara abastecimiento de este código, que no es el caso para dash, yashetc. Normalmente, si está utilizando bash, zsh, ksh, lo que sea - no debe preocuparse por esas cosas.
theoden8

¿Entonces estás contando bash como "ksh-like"? Entendido. Eso tiene más sentido
jpaugh

@jpaugh, bueno, eso es lo que quise decir porque kshel conjunto de características es principalmente un subconjunto de bashcaracterísticas (aunque no lo he comprobado a fondo).
theoden8

1

Haga lo siguiente para saber si su shell está usando Dash / Bash.

ls –la /bin/sh:

  • si el resultado es /bin/sh -> /bin/bash==> Entonces su shell está usando Bash.

  • si el resultado es /bin/sh ->/bin/dash==> Entonces su shell está usando Dash.

Si desea cambiar de Bash a Dash o viceversa, use el siguiente código:

ln -s /bin/bash /bin/sh (cambiar shell a Bash)

Nota : Si el comando anterior da como resultado un error que dice, / bin / sh ya existe, elimine / bin / sh e intente nuevamente.


0

Y se me ocurrió esto

sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ

¡Esto solo funcionará en Linux ! ¡No MacOS ni BSD!
F. Hauri

-1

Utilice amablemente el siguiente comando:

ps -p $$ | tail -1 | awk '{print $4}'

La primera es una tontería, echo $SHELLhace lo que estás tratando de hacer y hazlo bien. El segundo tampoco es bueno, porque $SHELLla variable de entorno contiene un shell predeterminado para un usuario actual, no un shell actualmente en ejecución. Si, por ejemplo, lo bashconfiguro como shell predeterminado, ejecutar zshy echo $SHELL, se imprimirá bash.
David Ferenczy Rogožan

Tienes razón Dawid Ferenczy, podemos usar el comando ps para determinar el shell actual. [# ps -p $$ | cola -1 | awk '{print $ 4}'].
Ranjithkumar T

echo $SHELLpuede ser basura: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
SaltwaterC

-2

Este funciona bien en Red Hat Linux (RHEL), macOS, BSD y algunos AIX :

ps -T $$ | awk 'NR==2{print $NF}' 

alternativamente, el siguiente también debería funcionar si pstree está disponible,

pstree | egrep $$ | awk 'NR==2{print $NF}'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.