GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi Bashrc vérifie-t-il si le shell actuel est interactif ?

Sur mon installation Arch, /etc/bash.bashrc et /etc/skel/.bashrc contenir ces lignes :

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

Sur Debian, /etc/bash.bashrc a :

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Et /etc/skel/.bashrc :

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

D'après man bash , cependant, les shells non interactifs ne lisent même pas ces fichiers :

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com‐
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file‐
   name.

Si je comprends bien, le *.bashrc les fichiers ne seront lus que si BASH_ENV est configuré pour pointer vers eux. C'est quelque chose qui ne peut pas arriver par hasard et ne se produira que si quelqu'un a explicitement défini la variable en conséquence.

Cela semble briser la possibilité que des scripts sourcent le .bashrc d'un utilisateur automatiquement en définissant BASH_ENV , quelque chose qui pourrait être utile. Étant donné que bash ne lira jamais ces fichiers lorsqu'il est exécuté de manière non interactive, sauf indication explicite de le faire, pourquoi la valeur par défaut *bashrc fichiers l'interdisent ?

Réponse acceptée :

C'est une question que j'allais poster ici il y a quelques semaines. Comme terdon , j'ai compris qu'un .bashrc ne provient que des shells Bash interactifs, il ne devrait donc pas y avoir besoin de .bashrc pour vérifier s'il s'exécute dans un shell interactif. Confusément, tous les distributions que j'utilise (Ubuntu, RHEL et Cygwin) avaient un certain type de vérification (test $- ou $PS1 ) pour s'assurer que le shell actuel est interactif. Je n'aime pas la programmation culte du cargo, alors j'ai commencé à comprendre le but de ce code dans mon .bashrc .

Bash a un cas particulier pour les shells distants

Après avoir étudié le problème, j'ai découvert que les shells distants sont traités différemment. Bien que les shells Bash non interactifs n'exécutent normalement pas ~/.bashrc commandes au démarrage, un cas particulier est fait lorsque le shell est appelé par un démon shell distant :

Bash tente de déterminer quand il est exécuté avec son entrée standard
connectée à une connexion réseau, comme lorsqu'il est exécuté par le démon shell distant
, généralement rshd , ou le démon shell sécurisé sshd . Si Bash
détermine qu'il est exécuté de cette manière, il lit et exécute les commandes
à partir de ~/.bashrc, si ce fichier existe et est lisible. Il ne le fera pas si
invoqué en tant que sh . Le --norc l'option peut être utilisée pour inhiber ce comportement,
et le --rcfile l'option peut être utilisée pour forcer la lecture d'un autre fichier, mais
ni rshd ni sshd invoquez généralement le shell avec ces options ou
autorisez-les à être spécifiées.

Exemple

Insérez ce qui suit au début d'un .bashrc distant . (Si .bashrc provient de .profile ou .bash_profile , désactivez-le temporairement pendant le test) :

echo bashrc
fun()
{
    echo functions work
}

Exécutez les commandes suivantes localement :

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Non i en $- indique que le shell est non interactif .
  • Pas de début - en $0 indique que le shell n'est pas un shell de connexion .

Fonctions Shell définies dans le .bashrc distant peut également être exécuté :

$ ssh remote_host fun
bashrc
functions work

J'ai remarqué que le ~/.bashrc est seulement source lorsqu'une commande est spécifiée comme argument pour ssh . Cela a du sens :lorsque ssh est utilisé pour démarrer un shell de connexion normal, .profile ou .bash_profile sont exécutés (et .bashrc n'est sourcée que si cela est explicitement fait par l'un de ces fichiers).

Connexe :Php:current — Renvoie l'élément courant dans un tableau

Le principal avantage que je peux voir d'avoir .bashrc lors de l'exécution d'une commande à distance (non interactive) est que les fonctions du shell peuvent être exécutées. Cependant, la plupart des commandes dans un .bashrc typique ne sont pertinents que dans un shell interactif, par exemple, les alias ne sont pas développés à moins que le shell ne soit interactif.

Les transferts de fichiers à distance peuvent échouer

Ce n'est généralement pas un problème lorsque rsh ou ssh sont utilisés pour démarrer un shell de connexion interactif ou lorsque des shells non interactifs sont utilisés pour exécuter des commandes. Cependant, cela peut être un problème pour les programmes tels que rcp , scp et sftp qui utilisent des shells distants pour transférer des données.

Il s'avère que le shell par défaut de l'utilisateur distant (comme Bash) est lancé implicitement lors de l'utilisation de scp commande. Il n'y a aucune mention de cela dans la page de manuel - seulement une mention que scp utilise ssh pour son transfert de données. Cela a pour conséquence que si le .bashrc contient toutes les commandes qui impriment sur la sortie standard, les transferts de fichiers échoueront , par exemple, scp échoue sans erreur.

Pourquoi scp et sftp échouer

SCP (Secure copy) et SFTP (Secure File Transfer Protocol) ont leurs propres protocoles pour que les extrémités locales et distantes échangent des informations sur le(s) fichier(s) en cours de transfert. Tout texte inattendu provenant de l'extrémité distante est (à tort) interprété comme faisant partie du protocole et le transfert échoue. D'après une FAQ du Snail Book

Ce qui arrive souvent, cependant, c'est qu'il y a des instructions dans les fichiers de démarrage
du système ou du shell par utilisateur sur le serveur (.bashrc , .profile , /etc/csh.cshrc , .login , etc.) qui génèrent des messages texte lors de la connexion,
destinés à être lus par des humains (comme fortune , echo "Hi there!" , etc.).

Un tel code ne devrait produire une sortie que sur les connexions interactives, lorsqu'il existe un tty attaché à l'entrée standard. S'il ne fait pas ce test, il va
insérer ces messages texte là où ils n'ont pas leur place :dans ce cas, polluant
le flux de protocole entre scp2 /sftp et sftp-server .

La raison pour laquelle les fichiers de démarrage du shell sont pertinents, c'est que sshd utilise le shell de l'utilisateur lors du démarrage de tout programme au nom de l'utilisateur (en utilisant par exemple /bin/sh -c "commande"). Il s'agit d'une tradition Unix, et présente
des avantages :

  • La configuration habituelle de l'utilisateur (alias de commande, variables d'environnement, umask,
    etc.) s'applique lorsque les commandes à distance sont exécutées.
  • La pratique courante consistant à définir le shell d'un compte sur /bin/false pour désactiver
    cela empêchera le propriétaire d'exécuter des commandes, si l'authentification
    réussissait toujours accidentellement pour une raison quelconque.

Détails du protocole SCP

Pour ceux qui s'intéressent aux détails du fonctionnement de SCP, j'ai trouvé des informations intéressantes dans Comment fonctionne le protocole SCP, qui inclut des détails sur Exécuter scp avec des profils de shell bavards du côté distant ? :

Par exemple, cela peut se produire si vous ajoutez ceci à votre profil shell sur le
système distant :

echo ""

Pourquoi ça bloque ? Cela vient de la façon dont scp dans source mode
attend la confirmation du premier message de protocole. S'il n'est pas binaire
0, il s'attend à ce qu'il s'agisse d'une notification d'un problème distant et attend
d'autres caractères pour former un message d'erreur jusqu'à ce que la nouvelle ligne arrive. Puisque
vous n'avez pas imprimé une autre nouvelle ligne après la première, votre scp local juste
reste dans une boucle, bloqué sur read(2) . En attendant, une fois le profil du shell
traité du côté distant, scp en mode récepteur a été démarré,
qui bloque également sur read(2) , attendant un zéro binaire indiquant le début
du transfert de données.

Conclusion / TLDR

La plupart des déclarations dans un .bashrc typique ne sont utiles que pour un shell interactif - pas lors de l'exécution de commandes distantes avec rsh ou ssh . Dans la plupart de ces situations, la définition de variables shell, d'alias et de fonctions de définition n'est pas souhaitée - et l'impression de n'importe quel texte vers la sortie standard est activement nuisible si le transfert de fichiers à l'aide de programmes tels que scp ou sftp . Quitter après avoir vérifié que le shell actuel n'est pas interactif est le comportement le plus sûr pour .bashrc .


Linux
  1. Faites des calculs dans le shell Linux avec GNU bc

  2. Le but de .bashrc et comment ça marche ?

  3. Que signifie la syntaxe |&en langage shell ?

  4. Pourquoi Ctrl + V ne colle-t-il pas dans Bash (shell Linux) ?

  5. Pourquoi le bit setuid fonctionne-t-il de manière incohérente ?

Comment installer Fish, le shell interactif convivial, sous Linux

Pourquoi le $shlvl commence-t-il au niveau 2 dans les shells sans connexion mais au niveau 1 dans les shells de connexion dans Rhel 7 ?

Pourquoi le Regex dans Bash ne fonctionne-t-il que s'il s'agit d'une variable et pas directement ? ?

Régénérer .bashrc à partir du shell actuel ?

Pourquoi ce pipeline shell sort-il ?

Pourquoi l'utilisateur 'bin' a-t-il besoin d'un shell de connexion ?