moyen compatible checkbashisms pour déterminer le shell actuel

18

Dans mon .profile, j'utilise le code suivant pour m'assurer que les alias et les fonctions liés à Bash ne proviennent que si le shell de connexion est en fait Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Je suis actuellement en train de mettre mes fichiers de configuration shell, mes scripts et mes fonctions sous contrôle de version. J'ai également récemment commencé le processus de suppression des bashismes occasionnels des scripts shell qui ne bénéficient pas des fonctionnalités spécifiques à Bash, par exemple, le remplacement function funcname()par funcname().

Pour mon référentiel de fichiers shell, j'ai configuré un hook de pré-validation qui exécute l' checkbashismsutilitaire à partir du paquet devscripts de Debian sur chaque shfichier du référentiel pour m'assurer que je n'introduis pas par inadvertance la syntaxe spécifique à Bash. Cependant, cela soulève une erreur pour mon .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Je me demandais s'il y avait un moyen de vérifier quel shell fonctionnait qui ne déclencherait pas d'avertissement checkbashisms.

J'ai vérifié la liste des variables liées au shell répertoriées par POSIX dans l'espoir que l'une d'entre elles puisse afficher le shell actuel. J'ai également examiné les variables définies dans un shell Dash interactif mais, encore une fois, je n'ai pas réussi à trouver un candidat approprié.

Pour le moment, j'ai exclu .profiled'être traité par checkbashisms; c'est un petit fichier donc il n'est pas difficile de le vérifier manuellement. Cependant, après avoir étudié le problème, j'aimerais toujours savoir s'il existe une méthode compatible POSIX pour déterminer quel shell est en cours d'exécution (ou au moins une manière qui ne provoque pas checkbashismsd'échec).


Contexte / clarification supplémentaire

L'une des raisons pour lesquelles je mets mes fichiers de configuration shell sous contrôle de version est de configurer régulièrement mon environnement sur tous les systèmes auxquels je me connecte actuellement: Cygwin, Ubuntu et CentOS (5 et 7, utilisant Active Directory pour l'utilisateur authentification). Je me connecte le plus souvent via X Windows / environnements de bureau et SSH pour les hôtes distants. Cependant, j'aimerais que ce soit à l'épreuve du temps et que je me fie le moins possible aux dépendances du système et à d'autres outils.

J'ai utilisé checkbashismscomme un simple test de santé mentale automatisé pour la syntaxe de mes fichiers liés au shell. Ce n'est pas un outil parfait, par exemple, j'ai déjà appliqué un patch pour qu'il ne se plaint pas de l'utilisation de command -vdans mes scripts. Pendant mes recherches, j'ai appris que le but réel du programme est d'assurer la conformité avec la politique Debian qui, si je comprends bien, est basée sur POSIX 2004 plutôt que 2008 (ou sa révision de 2013).

Anthony G - justice pour Monica
la source
Ce que vous faites est aussi conforme à POSIX que quelque chose peut l'être lorsque le but est de fonctionner différemment sur différents environnements conformes à POSIX. Votre boeuf est avec des checkbashisms.
Gilles 'SO- arrête d'être méchant'
Et d'ailleurs, je n'ai jamais utilisé de checkbashismes pour vérifier la portabilité de ma configuration. Je le vérifie en l'utilisant sur différents systèmes.
Gilles 'SO- arrête d'être méchant'
2
Et en passant, pour la chose spécifique que vous faites ici, écrivez un .bash_profilequi source à la fois .profileet (conditionnellement) .bashrc.
Gilles 'SO- arrête d'être méchant'
Merci pour le feedback @Gilles. C'est une solution très élégante pour le problème spécifique.
Anthony G - justice pour Monica

Réponses:

16

Votre

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

le code est entièrement conforme à POSIX et le meilleur moyen de vérifier que vous êtes en cours d'exécution bash. Bien sûr, la $BASH_VERSIONvariable est spécifique à bash, mais c'est précisément pourquoi vous l'utilisez! Pour vérifier que vous courez bash!

Notez que $BASH_VERSIONsera défini si bashest appelé comme bashou sh. Une fois que vous avez affirmé que vous exécutez bash, vous pouvez utiliser [ -o posix ]comme indicateur que le shell a été appelé sh(bien que cette option soit également définie lorsque POSIXLY_CORRECT est dans l'environnement ou bashest appelé avec -o posix, ou avec SHELLOPTS = posix dans l'environnement. Mais dans tous ces cas, bashse comportera comme s'il était appelé comme sh).


Une autre variable que vous pourriez utiliser à la place $BASH_VERSIONet qui checkbashismne semble pas vous plaindre à moins que l' -xoption ne soit passée $BASH. Cela est également spécifique à bashcelui que vous devriez également pouvoir utiliser pour déterminer si vous exécutez bashou non.


Je dirais également que ce n'est pas vraiment une bonne utilisation de checkbashisms. checkbashismsest un outil pour vous aider à écrire des shscripts portables (selon la shspécification de la politique Debian, un surensemble de POSIX), il aide à identifier la syntaxe non standard introduite par les personnes écrivant des scripts sur des systèmes où shest un lien symbolique bash.

A .profileest interprété par différents shells, dont beaucoup ne sont pas conformes à POSIX. En règle générale, vous n'utilisez pas shvotre shell de connexion, mais des shells comme zsh, fishou bashavec des fonctionnalités interactives plus avancées.

bashet zsh, lorsqu'ils ne sont pas appelés au fur shet à mesure que leur fichier de session de profil respectif ( .bash_profile, .zprofile) n'est pas conforme à POSIX (en particulier zsh) mais qu'il est toujours lu .profile.

Ce n'est donc pas la syntaxe POSIX que vous recherchez, .profilemais une syntaxe compatible avec POSIX (pour sh), bashet zshsi vous devez utiliser ces shells (peut-être même Bourne car le shell Bourne lit également .profilemais n'est pas communément trouvé sur les systèmes Linux) ).

checkbashismsvous aiderait certainement à trouver des bashismes, mais pourrait ne pas indiquer la syntaxe POSIX qui n'est pas compatible avec zshou bash.

Ici, si vous souhaitez utiliser du bashcode spécifique (comme le contournement de ce bashbogue qui ne lit pas ~/.bashrcdans les shells de connexion interactifs), une meilleure approche serait de le ~/.bash_profilefaire (avant ou après le sourcing ~/.profileoù vous placez votre session commune initialisations).

Stéphane Chazelas
la source
Cela ne passe pas à checkbashismscause de la variable utilisée.
Thomas Dickey
2
@ThomasDickey, oui, mais c'est un cas où le rapport de checkbashisms doit être ignoré. Toutes les autres solutions proposées jusqu'à présent sont bien pires.
Stéphane Chazelas
@ StéphaneChazelas Vous dites que toutes les autres solutions sont pires. Quel serait le problème avec l'utilisation $0dans ce cas particulier?
Anthony G - justice pour Monica
2
@AnthonyGeoghegan Cela ne fait pas de distinction entre bash appelé as shet un autre shell appelé as sh.
Gilles 'SO- arrête d'être méchant'
Il donne également un faux positif si dashou un autre shell non-bash a été appelé incorrectement argv[0] == "bash", ce qui est tout à fait légal, si peu probable.
Kevin
17

Habituellement, on utilise $0à cette fin. Sur le site que vous avez lié, il se trouve:

0
   (Zero.) Expands to the name of the shell or shell script.
jimmij
la source
Si simple! Je me suis tellement habitué à utiliser $0pour le nom d'un script shell, que j'ai nettoyé oublié qu'il peut également faire référence au shell actuel. Merci!
Anthony G - justice pour Monica
1
Comme d'habitude, cette convention est respectée par tous les programmes bien comportés, mais un programme malveillant peut jouer avec vous en utilisant divers appels système de la execfamille car ils permettent au programme appelant d'identifier le binaire à exécuter séparément de la chaîne à passer en argument. 0.
dmckee
Cela fonctionne pour le .profile :) cute
Rob
5

Habituellement, la variable d'environnement SHELL vous indique le shell par défaut. Vous ne devriez pas avoir besoin de source manuellement le fichier .bashrc (pas vrai voir la mise à jour ci-dessous), bash devrait le faire automatiquement juste assurez-vous qu'il se trouve dans le répertoire $ HOME.

L'autre option est de faire quelque chose comme:

 ps -o cmd= $$

qui vous indiquera la commande (sans arguments, le = sans valeur n'affichera pas les en-têtes de colonne) du processus en cours. Exemple de sortie:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

MISE À JOUR:

Je me suis trompé! :)

Le .bashrc n'est pas toujours fourni comme cela a été mentionné dans les commentaires ci-dessous et /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Vous pouvez donc déplacer votre .bashrc vers .bash_profile et voir si cela fonctionne sans avoir à faire le test. Sinon, vous avez le test ci-dessus.

Rob
la source
1
.bashrcn'est pas réellement fourni par un shell de connexion mais .profile(s'il existe) ou l' .bash_profileest. SHELLn'est pas spécifié par POSIX et malheureusement, l' cmdoption de formatage ne l'est pas non plus ps.
Anthony G - justice pour Monica
1
C'est ce que j'utiliserais, mais il est également sujet à des manipulations malveillantes.
dmckee
Notez que bien que le ps -o cmd= $$devrait fonctionner à peu près partout, la $SHELLvariable est complètement hors de propos. Il s'agit du shell par défaut de l'utilisateur et n'a aucune incidence sur le shell en cours d'exécution ou sur le shell exécutant un script de shell.
terdon
2
Non, pas vraiment. de nombreux shells liront ~/.profileau démarrage en tant que shells de connexion. Ainsi, par exemple, vous $SHELLpourriez l'être cshet vous courez bash -l. Cela démarrera bash en tant que shell de connexion, il sera donc lu ~/.profile, mais votre $SHELLpointera toujours vers csh. De plus, .profilepeut provenir explicitement d'un autre script. Étant donné que l'OP vise une robustesse maximale, toutes sortes de cas doivent être pris en compte. Dans tous les cas, $SHELLne fournit aucune information utile sur le shell en cours d'exécution.
terdon
2
FWIW, je me tiens aussi corrigé - à propos de SHELLne pas être spécifié par POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Anthony G - justice pour Monica
4

La question demande le shell de connexion de l'utilisateur ainsi que le shell actuel de manière apaisante checkbashisms. Si cela est censé être le shell sur lequel l'utilisateur s'est connecté, j'utiliserais celui de /etc/passwd, par exemple,

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Les utilisateurs peuvent bien sûr démarrer un nouveau shell après s'être connecté. Bien sûr, si l'un est bash et l'autre pas, les tests des variables d'environnement de bash peuvent ne pas aider.

Certains voudront peut-être utiliser getentplutôt quepasswd fichier (mais cela ne serait pas dans le cadre de la question).

Sur la base du commentaire sur LDAP et de la suggestion de logname, ce formulaire alternatif pourrait être utilisé:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

En le testant, j'ai remarqué qu'il lognamen'aime pas son entrée redirigée (j'ai donc laissé l'expression divisée). Une vérification rapide getentdevrait fonctionner sur les plateformes mentionnées (bien que cela aurait dû être fourni dans la question d'origine):

Thomas Dickey
la source
1
En supposant que la base de données utilisateur se trouve dans / etc / passwd (pas LDAP / NIS / mysql ...) et qu'il n'y a qu'un seul nom d'utilisateur par ID utilisateur. (il serait préférable de vérifier la première colonne $(logname)).
Stéphane Chazelas
iirc, POSIX ne définit pas l'utilitaire nécessaire (ou je l'aurais utilisé dans ma réponse). L'utilisation idou une variable modifiable par l'utilisateur est un choix différent.
Thomas Dickey
1
Notez que j'ai dit $(logname)non $LOGNAME. L'ID utilisateur et le shell de connexion sont tous deux dérivés du nom d'utilisateur utilisé lors de la connexion. Vous ne pouvez pas revenir de l'ID utilisateur au shell de connexion, sauf sur les systèmes où il n'y a qu'un seul nom d'utilisateur par ID utilisateur. Ou IOW, la clé primaire dans la base de données utilisateur est le nom d'utilisateur, pas l'uid.
Stéphane Chazelas
Merci @ThomasDickey pour une réponse venant d'une perspective différente. Cependant, c'est un peu plus complexe que ce à quoi je pensais (plus comme les réponses de Jimmij et Stéphane ). L'une des raisons pour lesquelles je mets mes fichiers de configuration shell sous contrôle de version est de configurer régulièrement mon environnement sur tous les systèmes auxquels je me connecte actuellement: Cygwin, Ubuntu et CentOS (5 et 7, utilisant Active Directory pour l'utilisateur authentification). Pour cette raison, je préfère garder la logique aussi simple que possible.
Anthony G - justice pour Monica