¿Cómo * * confiablemente * y * simplemente * obtengo el nombre actual del intérprete de shell?


9

Estoy buscando una forma simple y confiable de obtener el nombre del shell actual desde un script o un archivo de origen ( no desde la línea de comandos). Hubiera esperado hacerlo, $(basename "$SHELL")pero si mi shell de inicio de sesión es zshy tengo el siguiente código en some_script.sh

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

y lo ejecuto bash some_script.sh, todavía aparece en zshlugar de bashaunque el intérprete utilizado sea /bin/bash. No estoy seguro de por qué los diseñadores de shell eligieron mostrar el shell predeterminado en lugar del shell actual, pero eso parece ser a lo que nos hemos quedado atrapados.

Hay algunas otras preguntas ligeramente similares ( aquí y aquí ), pero sus respuestas se quedan cortas de muchas maneras.

  1. A menudo asumen que el usuario está tratando de descubrir qué shell interactivo están usando actualmente, pero eso es una locura. Yo sé lo que estoy shell comandos de escritura en - Necesito código que se puede ejecutar en cualquier lugar para ser capaz de determinar qué shell se está utilizando.
  2. A menudo dan varias cosas diferentes para probar, nuevamente como si estuvieras en la línea de comando y solo puedes jugar hasta que aprendas que estás usando bash, pero necesito una sola cosa que sea confiable en todos los contextos.
  3. A menudo dan cosas que son completamente inútiles de los scripts, como echo $0. Eso falla como se muestra en el script anterior. (Sí, funciona en una línea de comandos de shell interactiva, pero ¿por qué no sabes qué shell estás usando?)
  4. A veces dan comandos que (en mis pruebas limitadas) incluyen la información correcta, como ps -p $$, pero carecen de comandos sed / awk multiplataforma compatibles con shell cruzado para canalizarlo y obtener solo el nombre del shell e ignorar la otra información que aparece para el viaje.
  5. Incluyen cosas que solo funcionarán en un par de conchas, como $BASH_VERSIONy $ZSH_VERSION. Quiero apoyar a la mayor cantidad posible de proyectiles, tales como fish, csh, tcsh.

¿Cómo detecto de manera confiable y precisa cualquier shell actual ? Estoy buscando algo que funcione en todas las plataformas, en scripts y para la mayor cantidad de shells posibles y razonables 1 .

ACTUALIZACIÓN : Cuando publiqué esta pregunta, esperaba que hubiera alguna instalación integrada en los shells para darnos esta información, lo que parece no ser el caso. Dado que parece inevitable ahora confiar en algo fuera de los shells, debería hacer más explícito que estoy pidiendo unasolución multiplataforma (que, aunque implícito en mis objeciones a otras respuestas anteriores, podría ser fácil pasar por alto si no lo hace No leas la pregunta con cuidado).

Actualización 2 Si alguien todavía cree que este es un duplicado de la pregunta solo para Linux porque la respuesta de Stéphane no es solo para Linux, aquí están las diferencias entre lo que estoy pidiendo y lo que me ha proporcionado. (Tenga en cuenta que lo que ha escrito es ingenioso, y no lo estoy golpeando, pero no resuelve mi problema).

  1. Estoy buscando algo simple y confiable , que
  2. se puede agregar a cualquier secuencia de comandos o definición de función (que sería originada por un .zshrco.bash_profile o lo que sea) en rama.
  3. No puede utilizar su secuencia de comandos como una utilidad externa que pasa el nombre del intérprete a la secuencia de comandos / función que realiza la llamada, ya que siempre será interpretada por el intérprete predeterminado y la devolverá. Esto hace que sea difícil o imposible de usar para mis propósitos. Si es posible, todavía es muy muy difícil, y la solución para hacerlo funcionar no se da en la respuesta. Por lo tanto, no respondió a mi pregunta, por lo tanto, no es un duplicado.

Si quieres ver algo que va a trabajar, echar un vistazo a ShellDetective en GitHub . Eso puede hacer que sea más fácil ver las diferencias entre lo que ya está presente en SE y lo que está buscando esta pregunta (y de hecho fue escrita para satisfacer las necesidades de esta pregunta, que no se habían satisfecho en ningún otro lugar).


(PS si no se puede creer que hay un caso de uso para esto, imagínese las funciones que se sourced en .zshrc, .bash_profileo .profiledependiendo de lo que se está utilizando y qué servidor conchas que tiene disponibles. Están origen lo que no tienen línea de tinglado. Ellos son más útiles si pueden funcionar en cualquier shell, pero a veces tienen que saber en qué shell están para saber cómo comportarse).


1 No me interesan las "conchas" que no son conchas en ningún sentido tradicional de la palabra. No estoy preocupado por un caparazón que algún tipo escribió para sí mismo. Solo me preocupan los shells reales que realmente ocurren en la naturaleza, y que alguien podría tener que usar al iniciar sesión en un servidor en el que no pueden simplemente instalar los shells que quieran.


Para leer la discusión completamente fuera de tema sobre si Python es un shell: vea aquí
iconoclast

2
Citando la respuesta de Gilles a la pregunta ¿Cuál es la diferencia exacta entre un 'terminal', un 'shell', un 'tty' y una 'consola'?   - "Un shell es la interfaz principal que los usuarios ven cuando inician sesión, cuyo objetivo principal es iniciar otros programas". En mi opinión, eso es suficiente para eliminar Perl y Python de la categoría de "shell".
G-Man dice 'reinstalar a Monica' el

Relacionado vagamente: secuencias de comandos de compatibilidad: ¿Ahorrar $? para su uso posterior . Un enfoque sugerido: haga un resumen de las diferencias en las sintaxis de los diferentes shells diciendo sh -c "…", y luego haga la mayor parte del trabajo en la sintaxis de shell POSIX.
G-Man dice 'Reincorporar a Monica' el

2
¿Por qué quieres conseguir eso ? Algunos shells, utilizables como shells de inicio de sesión, tienen una sintaxis muy incompatible (¡algunos shells son lenguajes tipo Lisp!), Por lo que no entiendo por qué quieres hacer eso. Si está codificando archivos fuentebles, deberá codificar varias variantes y decirle a su usuario que elija la adecuada.
Basile Starynkevitch

1
Mi segunda pregunta es un ejemplo de disfrazar un caparazón. ¿Qué hacer si un script ejecutable es ejecutado por un intérprete cuyo nombre no coincide con el idioma que interpreta el intérprete? Si tomo una copia de csh, renómbrala y úsala fishpara ejecutar tu script, ¿quieres fisho csh?
Gilles 'SO- deja de ser malvado'

Respuestas:


5

He tenido excelentes resultados con esta combinación:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

En Tru64 (OSF / 1), la salida tiene paréntesis alrededor del shell. Tachuela tr -d '()'para eliminarlos.

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Parece funcionar en todos los shells, en Solaris 10 y RHEL 5.7 / 6.4. No probé otras distribuciones como Debian, Ubuntu o Mint, pero creo que todas deberían funcionar igual; Tampoco tengo idea si FreeBSD funcionaría.

ingrese la descripción de la imagen aquí


3
No funciona en scripts (devolverá el nombre del script en su lugar)
Qw3ry

2

Así que todavía estoy tratando de desarrollar esto, pero creo que tengo una idea que funcionará. Como ha notado, lo que está tratando de hacer es, si no imposible, extremadamente difícil de hacer en todos los shells (cada variante que agregue al políglota aumenta la complejidad a una velocidad mayor que la lineal). probablemente podría hacerlo si se divide en Bourne (sh, ash, dash, bash, ksh, pdksh, zsh, etc.) y c shells de estilo (csh, tcsh, fish, etc.) pero la variación entrecsh variantes presenta varias interesantes desafíos

Así que hagamos trampa y escribamos nuestra rutina de detección en un lenguaje conocido (bash con una línea shebang, C, perl, python, lo que sea) y determinemos el nombre del ejecutable del padre. entonces podemos usar dos trucos para devolver la información al padre, a saber, los valores de retorno y una sola palabra escrita en estándar. En sh shells descendientes, devolveríamos 0 y escribiríamos el nombre del shell ya que todos tienen un buen manejo de backtick. En la familia C de los shells podemos comenzar a incrementar el valor de retorno para cada conjunto de incompatibilidades que necesitamos solucionar. Los valores de retorno superiores a doscientos indicarían que los errores que comienzan con todo parecen estar bien, pero no puedo decir quién es mi padre en doscientos.

El programa interno parece fácil en Linux ( /proc/ppid/exees la mitad de la batalla). Estoy bastante seguro de que esto se puede hacer en bsd con opciones ps, pero no tengo un sistema bsd en ejecución en este momento para probar, y mi mac necesita un nuevo disco duro (y de todos modos es solo 10.4). Aunque reduce significativamente los problemas de sintaxis del shell, introduce un conjunto diferente de problemas de compatibilidad. Sigo pensando que tiene posibilidades.


Esta es la idea que finalmente decidí, que necesitas un programa externo para hacer el trabajo, aunque lo entendí cuando leí el comentario de G-Man sh -c "...", que tomó de otra pregunta . (Lo siento: me salteé tu respuesta al principio porque no vi ningún código en ella y solo volví y la leí más tarde)
Iconoclasta

Hay una paradoja en la pregunta de @ iconoclast. Quiere llamar a eso desde un script (tenga en cuenta que algunos shells no admiten funciones) que pueden obtenerse de cualquier shell, por lo que el script presumiblemente ya es políglota, por lo que ya debe tener algún soporte para descubrir qué es interpretarlo. En mi experiencia, escribir código políglota (mi which_intepreter es un ejemplo extremo, pero ver allí también para un solo shell es extremadamente difícil, y generalmente no vale la pena el esfuerzo.)
Stéphane Chazelas

@ StéphaneChazelas: Creo que tienes razón en que generalmente no vale la pena el esfuerzo (ciertamente en general no, y tal vez no siempre ) pero no estoy listo para rendirme ... Creo que el problema se divide en 2 problemas realmente: primero el nombre del shell y el segundo uso. Ambos (al menos prácticamente) requieren ayuda externa para evitar las limitaciones de los depósitos que podría estar utilizando. He resuelto el primer problema con una pequeña utilidad escrita en Crystal, y cuando encuentro tiempo, también quiero resolver el segundo. Por cierto, su solución es brillante, pero obviamente muy compleja.
iconoclasta

@iconoclas, suponiendo que pueda recuperar el nombre del shell de manera confiable (tenga en cuenta que la sustitución de comandos y la sintaxis de asignación de variables varía entre shells), no puede hacer cosas como case $shell in sh)...(esa es la sintaxis exclusiva de Bourne) o switch ($shell)(solo csh). test "$shell" = "sh" && do-somethingfunciona en csh/ sh/ rc/ es, pero no fish...
Stéphane Chazelas

Sí, ya me he encontrado con esto. Lo peor es que fish ni siquiera cargará un script que tenga una sintaxis que no le guste, por lo que no puede simplemente enviar STDERR /dev/null. Pero tengo una solución, que publicaré una vez que lo tenga todo funcionando.
iconoclasta

1

prueba algo como esto

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin

Esto falla en cshy tcsh.
iconoclasta

con una pequeña modificación que va a trabajar en fish, pero entonces sólo en fish, por desgracia: ps h -p %self -o args='' | cut -f1 -d' '. Esto crea un problema de huevo y gallina ... debes saber que estás fishen orden para ejecutar la fishversión del código ...
iconoclast

@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- funcionará con CSH pero no con los demás
DarkHeart

@DarkHeart: realidad que no, ya que la asignación de variables no era el problema en csh/ tcsh, fue esta parte: ps -p $$ -o args='' | cut -f1 -d' 'que los rendimientos -shen csh, y -cshen tcsh.
iconoclasta

1

Creo que no siempre puedes obtener el nombre actual del shell , y creo que debe tener en cuenta las limitaciones de lo que es posible.

En las distribuciones de Linux, la mayoría de los usuarios tendrían bash como inicio de sesión y shell interactivo (ya que bashes el shell predeterminado en la mayoría de las distribuciones). Algunos usuarios podrían establecer su concha a zsh, csh(y variantes) o parafish .

(Como otros comentarios y respuestas explican, la búsqueda de forma fiable la cáscara de bash, zsh, tcsh,fish que ya es un reto)

Sin embargo, algunos usuarios extraños pueden configurar la shell de acceso a algo totalmente diferente - un intérprete de Lisp, scsh, es, algún lenguaje de programación a la pitón, o Ocaml, o Perl, etc ...- y es su libertad para hacerlo. Probablemente, algunas personas están codificando su propio shell y usándolo de manera interactiva. Incluso si encuentra su extraño shell, no podrá hacer nada útil (por lo tanto, creo que no debería intentar obtener el nombre del shell).

Así que supongo que está codificando algún archivo fuente (quizás generando uno) para configurar algún software. Así que solo explique lo que está haciendo y codifique para el caso común de bash(y tal vez zsh& tcsh) ...


-2

use a continuación bajo su propia responsabilidad exención de responsabilidad

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}

me da una línea en blanco ... pero si entiendo lo que estás tratando de hacer, es muy inteligente
iconoclasta

No debería darle una línea en blanco, a menos que la -fopción esté activada readlink, algunas implementaciones no tienen ese interruptor
gwillie

Estoy en OS X, y la página del manual aparece -fcomo una opción disponible, tomando el formatcomo argumento.
iconoclasta

Eso puede funcionar en algunos casos especiales si $ 0 es un script (comienza con #! / ...) pero esa no es la configuración habitual. Aquí, la mayoría de los $ 0 apuntan a un archivo ELF.

readlinkno funciona en mi vm puredarwin, así que no tengo idea de lo que está sucediendo allí.
gwillie
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.