Comment obtenir * de manière fiable * et * simplement * le nom actuel de l'interpréteur de commandes?

9

Je cherche un moyen simple et fiable pour obtenir le nom du shell actuel à l'intérieur d'un script ou d'un fichier source ( pas à partir de la ligne de commande). Je m'attendrais à le faire, $(basename "$SHELL")mais si mon shell de connexion est zshet j'ai le code suivant dans some_script.sh

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

et je l'exécute avec bash some_script.sh, il répertorie toujours zshau lieu de bashmême si l'interpréteur utilisé l'est /bin/bash. Je ne sais pas pourquoi les concepteurs de shell ont choisi d'afficher le shell par défaut au lieu du shell actuel, mais cela semble être ce avec quoi nous sommes coincés.

Il y a d'autres questions légèrement similaires ( ici et ici ), mais leurs réponses sont insuffisantes à bien des égards.

  1. Ils supposent souvent que l'utilisateur essaie de comprendre quel shell interactif ils utilisent actuellement, mais c'est fou. Je sais dans quel shell je tape des commandes - j'ai besoin de code qui pourrait être exécuté n'importe où pour pouvoir déterminer dans quel shell il utilise.
  2. Ils donnent souvent plusieurs choses différentes à essayer - encore une fois comme si vous êtes sur la ligne de commande et pouvez simplement jouer jusqu'à ce que vous appreniez que vous utilisez bash - mais j'ai besoin d'une seule chose qui est fiable dans tous les contextes.
  3. Ils donnent souvent des choses totalement inutiles à partir de scripts, comme echo $0. Cela échoue comme indiqué dans le script ci-dessus. (Oui, cela fonctionne dans une ligne de commande de shell interactive, mais pourquoi ne sauriez-vous jamais quel shell vous utilisez?)
  4. Ils donnent parfois des commandes qui (dans mes tests limités) incluent les informations correctes, comme ps -p $$, mais manquent de commandes sed / awk multi-plateformes et compatibles shell pour les diriger pour obtenir uniquement le nom du shell et ignorer les autres informations qui viennent Pour le trajet.
  5. Ils incluent des choses qui ne fonctionneront que sur quelques coques, comme $BASH_VERSIONet $ZSH_VERSION. Je veux soutenir autant de coquilles que possible, comme fish, csh, tcsh.

Comment détecter de manière fiable et précise un shell actuel ? Je cherche quelque chose qui fonctionnera sur toutes les plateformes, dans les scripts, et pour autant de shells que possible et raisonnable 1 .

MISE À JOUR : Quand j'ai posté cette question, je m'attendais à ce qu'il y ait une installation intégrée dans les obus pour nous donner cette information, ce qui ne semble pas être le cas. Puisqu'il semble inévitable maintenant de s'appuyer sur quelque chose en dehors des coquilles, je devrais préciser que je demande unesolution multiplateforme (qui, bien que sous-entendue par mes objections aux autres réponses ci-dessus, pourrait être facile à manquer si vous ne lisez pas la question attentivement).

Mise à jour 2 Si quelqu'un pense toujours que c'est un double de la question Linux uniquement parce que la réponse de Stéphane n'est pas Linux uniquement, voici les différences entre ce que je demande et ce qu'il a fourni. (Notez que ce qu'il a écrit est ingénieux, et je ne le frappe pas, mais cela ne résout pas mon problème.)

  1. Je cherche quelque chose de simple et fiable , qui
  2. peut être ajouté à une définition de script ou la fonction (qui proviendraient par un .zshrcou .bash_profileou autre) à la branche.
  3. Vous ne pouvez pas utiliser son script comme un utilitaire externe qui transmet le nom de l'interpréteur au script / fonction appelant, car il sera toujours interprété par l'interpréteur par défaut et le renverra. Cela rend son utilisation difficile ou impossible à mes fins. Si c'est possible, c'est encore très très difficile, et la solution pour le faire fonctionner n'est pas donnée dans la réponse. Il n'a donc pas répondu à ma question, il ne s'agit donc pas d'un doublon.

Si vous voulez voir quelque chose qui va travailler, jetez un oeil à ShellDetective sur GitHub . Cela peut permettre de voir plus facilement les différences entre ce qui est déjà présent sur SE et ce que recherche cette question (et a en fait été écrite afin de répondre aux besoins de cette question, qui n'étaient satisfaits nulle part ailleurs).


(PS si vous ne pouvez pas croire qu'il ya un cas d'utilisation pour cela, imaginez les fonctions qui se sourced dans .zshrc, .bash_profileou en .profilefonction de ce serveur est utilisé et ce que les coquilles dont il dispose. Ils sont source de sorte qu'ils ont pas de ligne de tralala. Ils sont plus utiles s'ils peuvent fonctionner dans n'importe quel shell, mais parfois ils doivent savoir dans quel shell ils se trouvent pour savoir comment se comporter.)


1 Je ne m'intéresse pas aux "obus" qui ne sont pas des obus au sens traditionnel du terme. Je ne suis pas préoccupé par un obus qu'un gars a écrit pour lui-même. Je ne m'intéresse qu'aux vrais shells qui se produisent réellement dans la nature, et que quelqu'un pourrait éventuellement utiliser lors de la connexion à un serveur sur lequel il ne peut pas simplement installer les shells qu'il souhaite.

iconoclaste
la source
Pour lire la discussion entièrement hors sujet sur la question de savoir si Python est un shell: voir ici
iconoclaste
2
Citant la réponse de Gilles à la question Quelle est la différence exacte entre un «terminal», un «shell», un «tty» et une «console»?   - «Un shell est l'interface principale que les utilisateurs voient lorsqu'ils se connectent, dont le but principal est de démarrer d'autres programmes.» OMI, c'est suffisant pour éliminer Perl et Python de la catégorie des «shell».
G-Man dit «Réintègre Monica»
Vaguement lié: Script de compatibilité: Économisez $? pour une utilisation ultérieure . Une approche suggérée: faites le tour des différences dans les syntaxes des différents shells en disant sh -c "…", puis faites l'essentiel du travail dans la syntaxe du shell POSIX.
G-Man dit 'Réinstalle Monica'
2
Pourquoi voulez-vous obtenir cela ? Certains shells, utilisables comme shells de connexion, ont une syntaxe très incompatible (certains shells sont des langages de type Lisp!), Donc je ne comprends pas pourquoi vous voulez faire ça. Si vous codez des fichiers sourcables, vous devrez coder plusieurs variantes et dire à votre utilisateur de choisir celle qui convient.
Basile Starynkevitch
1
Ma deuxième question est un exemple de déguisement d'un obus. Que faire si un script est exécuté par un exécutable interpréteur dont le nom ne correspond pas à la langue interprétée par l'interpréteur? Si je prends une copie de csh, je la renomme fishet je l'utilise pour exécuter votre script, voulez-vous fishou csh?
Gilles 'SO- arrête d'être méchant'

Réponses:

5

J'ai eu d'excellents résultats avec cette combinaison:

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

Sur Tru64 (OSF / 1), la sortie a des parenthèses autour du shell. Tack tr -d '()'pour les supprimer.

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

Semble fonctionner sur tous les shells, sur Solaris 10 et RHEL 5.7 / 6.4. Je n'ai pas testé d'autres distributions telles que Debian, Ubuntu ou Mint, mais je pense qu'elles devraient toutes fonctionner de la même manière; Je n'ai également aucune idée si FreeBSD fonctionnerait.

entrez la description de l'image ici

Chris Embry
la source
3
Ne fonctionne pas dans les scripts (retournera le nom du script à la place)
Qw3ry
2

Donc j'essaie toujours d'étoffer cela, mais je pense avoir une idée qui fonctionnera. Comme vous l'avez remarqué, ce que vous essayez de faire est sinon impossible, extrêmement difficile à faire dans tous les shells (chaque variante que vous ajoutez au polyglotte augmente la complexité à un rythme supérieur à linéaire). vous pourriez probablement le faire si vous vous divisez en Bourne (sh, ash, dash, bash, ksh, pdksh, zsh, etc.) et des coquilles de style c (csh, tcsh, fish, etc.), mais la variation entre les cshvariantes présente diverses intéressantes défis.

Alors trichons et écrivons notre routine de détection dans un langage connu (bash avec une ligne shebang, C, perl, python, peu importe) et déterminons le nom de l'exécutable du parent. nous pouvons ensuite utiliser deux astuces pour renvoyer les informations au parent, à savoir les valeurs de retour et un seul mot écrit en sortie standard. Sur les sh sh descendus, nous retournerions 0 et écrivions le nom du shell car ils ont tous une bonne gestion du backtick. sur la famille C de shells, nous pouvons commencer à incrémenter la valeur de retour pour chaque ensemble d'incompatibilités que nous devons contourner. Des valeurs de retour supérieures à deux cents indiqueraient que des erreurs commençant par tout semblent correctes, mais je ne peux tout simplement pas dire qui est mon parent à deux cents.

Le programme interne semble facile sur Linux ( /proc/ppid/exec'est la moitié de la bataille). Je suis presque sûr que cela peut être fait sur bsd avec des options ps, mais je n'ai pas de système bsd en cours d'exécution pour le moment, et mon mac a besoin d'un nouveau disque dur (et n'est que de 10,4 de toute façon). Bien qu'il réduise considérablement les problèmes de syntaxe du shell, il introduit un ensemble différent de problèmes de compatibilité. Je pense toujours qu'il a des possibilités.

hildred
la source
C'est l'idée que j'ai finalement trouvée - que vous avez besoin d'un programme extérieur pour faire le travail - bien que je l'ai obtenue en lisant le commentaire de G-Man sh -c "...", qu'il a tiré d' une autre question . (Désolé: j'ai ignoré votre réponse au début parce que je n'y ai pas vu de code, et je ne suis revenu que plus tard.)
iconoclaste du
Il y a un paradoxe dans la question de @ iconoclast. Il veut appeler cela à partir d'un script (notez que certains shells ne prennent pas en charge les fonctions) qui peuvent provenir de n'importe quel shell, de sorte que le script est probablement déjà polyglotte, donc doit déjà avoir un certain support pour comprendre ce qui l'interprète. D'après mon expérience, écrire du code polyglotte (mon which_intepreter est un exemple extrême, mais y voir aussi pour un shell uniquement est extrêmement difficile, et ne vaut généralement pas la peine.
Stéphane Chazelas
@ StéphaneChazelas: Je pense que vous avez raison qu'il est généralement ne vaut pas l'effort (certainement généralement pas, et peut - être toujours pas) mais je ne suis pas prêt à abandonner ... Je pense que les pauses problème en 2 problèmes vraiment: 1er obtenir le nom du shell, et 2ème en l'utilisant. Les deux (au moins pratiquement) nécessitent une aide extérieure pour éviter les limitations des coques que vous pourriez utiliser. J'ai résolu le premier problème avec un petit utilitaire écrit en Crystal, et quand je trouve du temps, je veux aussi résoudre le 2ème. Btw, votre solution est brillante, mais évidemment très complexe.
iconoclaste du
@iconoclas, en supposant que vous pouvez récupérer le nom du shell de manière fiable (notez que la syntaxe de substitution de commande et d'affectation de variable varie entre les shells), vous ne pouvez pas faire des choses comme case $shell in sh)...(c'est la syntaxe Bourne uniquement), ou switch ($shell)(csh uniquement). test "$shell" = "sh" && do-somethingtravaille dans csh/ sh/ rc/ es, mais pas fish...
Stéphane Chazelas
oui, je l'ai déjà rencontré. Le pire, c'est que fish ne chargera même pas un script dont la syntaxe ne lui plaît pas, vous ne pouvez donc pas simplement envoyer STDERR à /dev/null. Mais j'ai une solution, que je publierai une fois que tout aura fonctionné.
iconoclaste du
1

essayez quelque chose comme ça

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin
Dany Flores
la source
Cela échoue dans cshet tcsh.
iconoclaste du
avec une petite modification , il fonctionnera dans fish, mais seulement fish, malheureusement: ps h -p %self -o args='' | cut -f1 -d' '. Cela crée un problème de poule et d'oeuf ... vous devez savoir que vous êtes fishdans le but d'exécuter la fishversion du code ...
iconoclaste
@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- fonctionnera avec CSH mais pas avec les autres
DarkHeart
@DarkHeart: en fait qui ne parvient pas à, puisque l'affectation variable n'était pas le problème csh/ tcsh, il était cette partie: ps -p $$ -o args='' | cut -f1 -d' 'qui retourne -shsur csh, et -cshsur tcsh.
iconoclaste du
1

Je crois que vous ne pouvez pas toujours obtenir le nom du shell actuel , et je pense que vous devez être conscient des limites de ce qui est possible.

Sur les distributions Linux, la plupart des utilisateurs auraient bash comme login et shell interactif (car bashc'est le shell par défaut sur la plupart des distributions). Certains utilisateurs définiraient leur shell sur zsh, csh(et variantes) ou sur fish.

(Comme d' autres commentaires et réponses expliquent, trouver de manière fiable la coquille sur bash, zsh, tcsh, fishest déjà difficile)

Mais certains utilisateurs étranges peuvent définir leur shell de connexion à tout autre chose - un interprète Lisp, scsh, es, un langage de script à la Python, ou Ocaml ou Perl, etc ...- et il est leur liberté de le faire. Probablement, certaines personnes codent leur propre shell et l'utilisent de manière interactive. Même si vous avez trouvé leur étrange coquille, vous ne pourrez rien faire d'utile (donc je pense que vous ne devriez pas essayer d'obtenir le nom de la coquille).

Je suppose donc que vous codez un fichier source (peut-être en générant un) pour configurer certains logiciels. Il suffit donc d'expliquer ce que vous faites et de coder pour le cas commun de bash(et peut-être zsh& tcsh) ....

Basile Starynkevitch
la source
-2

utiliser ci-dessous à ses propres risques

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}
gwillie
la source
cela me donne une ligne blanche ... mais si je comprends ce que vous essayez de faire, c'est très intelligent
iconoclaste
Il ne devrait pas vous donner de ligne vierge, sauf -foption readlink, certaines implémentations n'ont pas ce commutateur
gwillie
Je suis sur OS X, et la page de manuel -fest une option disponible, prenant l' formatargument comme.
iconoclaste
Cela peut fonctionner dans certains cas spéciaux si $ 0 est un script (commence par #! / ...) mais ce n'est pas la configuration habituelle. Ici, la plupart des $ 0 pointent vers un fichier ELF.
readlinkne fonctionne pas sur mon puredarwin vm donc je ne sais pas ce qui se passe là-bas.
gwillie