Sur mon installation Arch, /etc/bash.bashrc
et /etc/skel/.bashrc
contiennent 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
Selon 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 réglé 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 rompre la possibilité d'avoir des scripts source d'un utilisateur .bashrc
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
les fichiers ne le permettent pas?
C'est une question que j'allais poster ici il y a quelques semaines. Comme terdon, j'ai compris qu'un .bashrc
Est uniquement fourni pour des shells Bash interactifs donc il ne devrait pas être nécessaire que .bashrc
Vérifie s'il fonctionne dans un Shell interactif. De façon confuse, toutes les les distributions que j'utilise (Ubuntu, RHEL et Cygwin) ont eu un certain type de vérification (test $-
Ou $PS1
) pour garantir que le shell actuel est interactif. Je n'aime pas programmation culte de la cargaison alors j'ai commencé à comprendre l'objectif de ce code dans mon .bashrc
.
Après avoir étudié le problème, j'ai découvert que shells distants sont traités différemment. Bien que les shells Bash non interactifs n'exécutent pas normalement les commandes ~/.bashrc
Au démarrage, un cas spécial est fait lorsque le shell est invoqué par le 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 s'il est appelé en tant quesh
. L'option--norc
Peut être utilisée pour inhiber ce comportement et l'option--rcfile
Peut être utilisée pour forcer la lecture d'un autre fichier, mais nirshd
nisshd
invoque généralement le Shell avec ces options ou permet de les spécifier.
Insérez ce qui suit au début d'une télécommande .bashrc
. (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
i
dans $-
Indique que le shell est non interactif.-
En tête dans $0
Indique que le shell n'est pas un shell de connexion.Les fonctions shell définies dans la télécommande .bashrc
Peuvent également être exécutées:
$ ssh remote_Host fun
bashrc
functions work
J'ai remarqué que le ~/.bashrc
Provient seulement 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 fourni que si cela est explicitement fait par l'un de ces fichiers).
Le principal avantage que je peux voir à avoir .bashrc
Sourced lors de l'exécution d'une commande à distance (non interactive) est que les fonctions Shell peuvent être exécutées. Cependant, la plupart des commandes dans un .bashrc
Typique ne sont pertinentes que dans un shell interactif, par exemple, les alias ne sont développés que si le shell est interactif.
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 des programmes tels que rcp
, scp
et sftp
qui utilisent des shells distants pour le transfert de données.
Il s'avère que le shell par défaut de l'utilisateur distant (comme Bash) est implicitement démarré lors de l'utilisation de la commande scp
. 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 des commandes qui s'impriment sur la sortie standard, les transferts de fichiers échoueront , par exemple, scp échoue sans erreur .
Voir aussi ce rapport de bogue Red Hat relatif d'il y a 15 ans, scp se casse quand il y a une commande echo dans/etc/bashrc (qui a finalement été fermé par WONTFIX
).
scp
et sftp
échouentSCP (Secure copy) et SFTP (Secure File Transfer Protocol) ont leurs propres protocoles pour que les extrémités locale et distante échangent des informations sur le ou les fichiers transférés. Tout texte inattendu de l'extrémité distante est (à tort) interprété comme faisant partie du protocole et le transfert échoue. Selon un FAQ du Snail Book
Ce qui se produit souvent, cependant, c'est qu'il y a des instructions dans le système ou dans les fichiers de démarrage Shell par utilisateur sur le serveur (
.bashrc
,.profile
,/etc/csh.cshrc
,.login
, Etc.) qui produisent des messages texte à la connexion, destinés à être lus par les humains (commefortune
,echo "Hi there!"
, Etc.).Un tel code ne devrait produire une sortie sur les connexions interactives que lorsqu'un
tty
est attaché à l'entrée standard. S'il ne fait pas ce test, il insérera ces SMS là où ils n'appartiennent pas: dans ce cas, polluer le flux de protocole entrescp2
/sftp
etsftp-server
.La raison pour laquelle les fichiers de démarrage du shell sont pertinents 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.) est en vigueur lorsque des commandes distantes sont exécutées.
- La pratique courante de définir le shell d'un compte sur/bin/false pour le désactiver empêchera le propriétaire d'exécuter des commandes, si l'authentification réussit encore accidentellement pour une raison quelconque.
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 comprend des détails sur Exécuter scp avec des profils Shell bavards sur la télécommande côté? :
Par exemple, cela peut se produire si vous ajoutez ceci à votre profil Shell sur le système distant:
écho ""
Pourquoi ça se bloque? Cela vient de la façon dont
scp
en mode source attend la confirmation du premier message de protocole. S'il ne s'agit pas d'un 0 binaire, il s'attend à ce qu'il s'agisse d'une notification d'un problème distant et attend que plus de caractères forment un message d'erreur jusqu'à ce que la nouvelle ligne arrive. Comme vous n'avez pas imprimé une autre nouvelle ligne après la première, votrescp
local reste simplement dans une boucle, bloqué surread(2)
. En attendant, après le traitement du profil Shell sur le côté distant,scp
en mode récepteur a été démarré, qui se bloque également surread(2)
, en attendant un zéro binaire indiquant le début des données transfert.
La plupart des instructions 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, il n'est pas souhaitable de définir des variables Shell, des alias et de définir des fonctions - et l'impression n'importe quel texte à 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
.
La page de manuel oublie de mentionner que bash
source également .bashrc
pour les shells distants non interactifs, comme dans
ssh hostname command
http://git.savannah.gnu.org/cgit/bash.git/tree/Shell.c#n101
COMMAND EXECUTE BASHRC
--------------------------------
bash -c foo NO
bash foo NO
foo NO
rsh machine ls YES (for rsh, which calls 'bash -c')
rsh machine foo YES (for Shell started by rsh) NO (for foo!)
echo ls | bash NO
login NO
bash YES
http://git.savannah.gnu.org/cgit/bash.git/tree/Shell.c#n105
/* If we were run by sshd or we think we were run by rshd, execute
~/.bashrc if we are a top-level Shell. */
if ((run_by_ssh || isnetconn (fileno (stdin))) && Shell_level < 2)
{
maybe_execute_file (SYS_BASHRC, 1);
maybe_execute_file (bashrc_file, 1);
Par convention, .bashrc
est l'endroit où l'utilisateur stocke la configuration personnalisée du Shell.
Ces configurations personnalisées peuvent être des variables d'environnement, des alias, une invite de fantaisie. Avec un shell non interactif, ces petites choses n'ont aucun sens. De plus, un Shell non interactif peut être appelé dans de nombreux contextes, vous n'êtes pas sûr que ces variables d'environnement puissent conduire à des faux négatifs, voire à des vulnérabilités.
Un exemple le plus proche est un alias comme:
alias cp='cp -i'
Ensuite, il bloque votre Shell non interactif pour toujours.
Ainsi, la vérification s'effectue en haut de .bashrc
pour éviter tout problème.
Parce que le shell peut être appelé comme non interactif login Shell, donc bloquez explicitement le sourcing *bashrc
aucun sens.
Lorsque le shell était appelé shell de connexion non interactif , il source /etc/profile
, puis sourcez le premier trouvé dans ~/.bash_profile
, ~/.bash_login
, et ~/.profile
:
Lorsque bash est invoqué en tant que shell de connexion interactif, ou en tant que shell non interactif avec l'option --login , il lit et exécute d'abord les commandes à partir du fichier/etc/profile, si ce fichier existe. Après avoir lu ce fichier, il recherche ~/.bash_profile, ~/.bash_login et ~/.profile, dans cet ordre, et lit et exécute les commandes du premier qui existe et est lisible.
Rien n'empêche ces fichiers de s'approvisionner .bashrc
lui-même, faisant ainsi la vérification à l'intérieur .bashrc
est plus sûr et simplifie les choses.