Pourquoi les shells interactifs sur les shells de connexion OSX sont-ils par défaut?

43

Sous Linux et, à ma connaissance, sur tous les systèmes Unix, les émulateurs de terminaux exécutent par défaut des shells interactifs sans connexion. Cela signifie que, pour bash, le shell démarré va:

Lorsqu'un shell interactif qui n'est pas un shell de connexion est démarré, bash lit et exécute les commandes à partir de /etc/bash.bashrcet ~/.bashrc, si ces fichiers existent. Cela peut être inhibé en utilisant l' --norc option.

L' --rcfile option de fichier forcera bash à lire et à exécuter des commandes à partir du fichier au lieu de /etc/bash.bashrcet ~/.bashrc.

Et pour les coquilles de connexion:

Lorsque bash est appelé en tant que shell de connexion interactif ou en tant que shell non interactif avec l' --loginoption, il commence par lire et exécuter les commandes du fichier /etc/profile, si ce fichier existe. Après avoir lu ce fichier, il recherche ~/.bash_profile, ~/.bash_loginet ~/.profile, dans cet ordre, et lit et exécute les commandes de la première qui existe et est lisible.

Cette --noprofileoption peut être utilisée au démarrage du shell pour empêcher ce comportement.

Sur Mac OS X, cependant, le shell par défaut ( ce qui est bash) a commencé dans le terminal par défaut (Terminal.app) en fait des sources ~/.bash_profileou ~.profileetc. En d' autres termes, il agit comme un shell de connexion.

Question principale : Pourquoi le shell interactif par défaut est-il un shell de connexion sous OSX? Pourquoi OSX a-t-il choisi de faire cela? Cela signifie que toutes les instructions / tutoriels pour les choses basées sur un shell qui mentionnent des changements ~/.bashrcvont échouer sous OSX ou vice versa ~/.profile. Pourtant, si de nombreuses accusations peuvent être portées contre Apple, le recrutement de développeurs incompétents ou idiots n'en fait pas partie. Vraisemblablement, ils avaient une bonne raison pour cela, alors pourquoi?

Sous-questions: Terminal.app exécute-t-il réellement un shell de connexion interactif ou ont-ils changé le comportement de bash? Est-ce spécifique à Terminal.app ou est-il indépendant de l'émulateur de terminal?

terdon
la source
2
Terminal.app exécute un shell de connexion. Je ne sais pas pourquoi Apple a choisi de le faire.
Gilles 'SO- arrête d'être méchant'
En passant, à partir de macOS Catalina , le shell par défaut est zsh .
Basil Bourque

Réponses:

33

La façon dont il est censé le travail est que, au moment où vous obtenez une invite du shell, à la fois .profileet .bashrcont été exécutés. Les détails spécifiques de la manière dont vous arrivez à ce point sont d'une importance secondaire, mais si l'un des fichiers n'était pas exécuté, vous auriez un shell avec des paramètres incomplets.

La raison pour laquelle les émulateurs de terminal sous Linux (et d’autres systèmes X) n’ont pas besoin de s’exécuter .profileeux-mêmes est qu’elle a normalement déjà été exécutée lorsque vous vous êtes connecté à X. Les paramètres .profilesont supposés être du type pouvant être utilisé. hérité par les sous-processus, donc tant qu'il est exécuté une fois lorsque vous vous connectez (par exemple via .Xsession), aucun sous-shell supplémentaire n'a besoin de le réexécuter.

Comme l'explique la page wiki Debian liée par Alan Shutko:

« Pourquoi .bashrcun fichier séparé .bash_profile, alors? Cela se fait principalement pour des raisons historiques, quand les machines ont été extrêmement lentes par rapport aux postes de travail d'aujourd'hui. Le traitement des commandes .profileou .bash_profilepourrait prendre un temps assez long, surtout sur une machine où beaucoup de travail les commandes externes de configuration difficiles, qui créent des variables d’environnement pouvant être transmises à des processus enfants, sont placées dans .bash_profileles paramètres transitoires et les alias non hérités. en .bashrcsorte qu'ils puissent être relus par tous les sous - shell « .

Les mêmes règles sont également valables sous OSX, à une exception près: l'interface graphique OSX ne s'exécute pas .profilelorsque vous vous connectez, apparemment parce qu'elle possède sa propre méthode de chargement des paramètres globaux. Mais cela signifie qu’un émulateur de terminal sous OSX doit être exécuté .profile(en indiquant au shell qu’il lance qu’il lance est un shell de connexion), sinon vous vous retrouveriez avec un shell potentiellement endommagé.


Maintenant, une sorte de particularité idiote de bash, que ne partagent pas la plupart des autres shells, est qu'elle ne s'exécutera pas automatiquement .bashrcsi elle est lancée en tant que shell de connexion. La solution standard consiste à inclure quelque chose comme les commandes suivantes .bash_profile:

[[ -e ~/.profile ]] && source ~/.profile    # load generic profile settings
[[ -e ~/.bashrc  ]] && source ~/.bashrc     # load aliases etc.

Alternativement, il est possible de ne pas en avoir .bash_profiledu tout et d'inclure simplement du code spécifique à Bash dans le .profilefichier générique à exécuter .bashrcsi nécessaire.

Si OSX par défaut .bash_profileou .profile ne le fait pas , c'est sans doute un bogue. Dans tous les cas, la solution appropriée consiste simplement à ajouter ces lignes .bash_profile.


Edit: Comme le notent les critiques , le shell par défaut sous OSX était tcsh, dont le comportement est beaucoup plus sain à cet égard: lorsqu'il est exécuté en tant que shell de connexion interactif, tcsh lit automatiquement les deux .profile et .tcshrc / .cshrc, et n'a donc pas besoin de solutions de rechange comme le .bash_profiletruc. montré ci-dessus.

Sur cette base, je suis sûr à 99% que OSX .bash_profilen’a pas fourni le paramètre par défaut approprié , car lorsqu’ils sont passés de tcsh à bash, les employés d’Apple n’ont tout simplement pas remarqué cette petite verrue dans le comportement de bash au démarrage. Avec tcsh, aucune de ces astuces n’était nécessaire: démarrer tcsh en tant que shell de connexion à partir d’un émulateur de terminal OSX, Just Plain Works, fait ce qu’il faut sans ces kluges.

Ilmari Karonen
la source
1
Merci, je savais en quelque sorte tout cela. Ma question est la suivante: pourquoi OSX a-t-il choisi d’être configuré de manière à rendre .bashrcnon pertinent? Pourquoi ont-ils choisi de faire en sorte que tous les obus se connectent? Autant que je sache, seule votre dernière phrase parle de cela et seulement pour dire que c'est un bug.
terdon
1
Non, le problème est qu'ils démarrent des shells de connexion dans leurs émulateurs de terminaux. Les fichiers de points sont le comportement bash par défaut, le démarrage des shells de connexion lira le profil, etc., et non pas le bashrc, etc.
terdon
2
Oui, et Alan et moi avons expliqué que c'est parce que, contrairement à Linux, OSX ne s'exécute pas .profilelorsque l'utilisateur se connecte à l'interface graphique. Ils doivent donc l'exécuter ultérieurement pour obtenir des variables d'environnement telles que $PATH, qui sont normalement définies dans .profile, configurées correctement. . Le fait que, comme effet secondaire, cela .bashrcne cause pas de source est un bug; vous pouvez discuter du fait qu'il s'agisse d'un bogue dans bash ou dans OSX, mais cela ne change rien au fait que le comportement correct consiste à s'assurer que les variables d'environnement de .profileet le paramètre de configuration de bash .bashrcsont chargés.
Ilmari Karonen
1
Avoir la .bashrcsource .profileserait une mauvaise idée car cela provoquerait une nouvelle exécution de chaque sous-shell .profile. Si rien d'autre, cela causerait des .profileidiomes communs comme export PATH = "$HOME/bin:$PATH"garder des entrées redondantes avant les autres $PATH. Avoir une .profilesource .bashrcest beaucoup plus logique, mais seulement après avoir vérifié que le shell sous lequel il est exécuté est, en fait, bash. Avoir à la .bash_profilefois la source .profile et .bashrc , comme je l’ai suggéré ci-dessus, l’OMI, l’option la plus judicieuse.
Ilmari Karonen
2
Et je dis la réponse à "pourquoi utilisent-ils un shell de connexion?" est « parce qu'ils ont besoin de la charge .profile», alors que la réponse à « pourquoi ne pas la source .bashrcde .profile, alors? » est "parce que c'est un bug!" Sérieusement, cela ne peut éventuellement être une décision intentionnelle; c'est juste quelque chose qu'ils ont oublié, probablement parce que, dans l'écosystème Mac, les coquilles sont des citoyens de seconde classe que la plupart des utilisateurs ne sont pas supposés traiter. (Ps. Voir aussi la réponse du candidat pour une explication historique; il s'agit presque certainement d'une régression du passage de tcsh à bash.)
Ilmari Karonen
14

La raison principale pour laquelle les applications de terminal X exécutent des shells autres que de connexion par défaut est qu’au début des temps, votre .Xsession aurait exécuté le fichier .profile pour configurer vos éléments de connexion initiaux. Puis, comme tout était déjà configuré, les applications de terminal n’avaient pas besoin de s’exécuter, elles pouvaient exécuter le fichier .bashrc. La discussion sur les raisons pour lesquelles cela est important est à l' adresse https://wiki.debian.org/DotFiles :

Prenons xdm comme exemple. Un jour, Pierre revient de vacances un jour et découvre que son administrateur système a installé xdm sur le système Debian. Il se connecte parfaitement et xdm lit son fichier .xsession et exécute Fluxbox. Tout semble aller pour le mieux jusqu'à ce qu'il reçoive un message d'erreur dans la mauvaise localisation! Puisqu'il remplace la variable LANG dans son fichier .bash_profile et que xdm ne lit jamais .bash_profile, sa variable LANG est maintenant définie sur en_US au lieu de fr_CA.

Maintenant, la solution naïve à ce problème est qu'au lieu de lancer "xterm", il pourrait configurer son gestionnaire de fenêtres pour lancer "xterm -ls". Cet indicateur indique à xterm qu’au lieu de lancer un shell normal, il devrait lancer un shell de connexion. Sous cette configuration, xterm génère / bin / bash, mais ajoute "- / bin / bash" (ou peut-être "-bash") dans le vecteur d'argument, ainsi bash se comporte comme un shell de connexion. Cela signifie que chaque fois qu'il ouvrira un nouveau xterm, il lira / etc / profile et .bash_profile (comportement bash intégré), puis .bashrc (car .bash_profile le fera). Cela peut sembler fonctionner correctement au début - ses fichiers de points ne sont pas lourds, donc il ne remarque même pas le retard - mais il y a un problème plus subtil. Il lance également un navigateur Web directement à partir de son menu Fluxbox, et le navigateur Web hérite de la variable LANG de fluxbox, qui est maintenant définie sur la mauvaise localisation. Ainsi, bien que ses xterms puissent fonctionner correctement et que tout ce qui est lancé à partir de ses serveurs xterms puisse fonctionner correctement, son navigateur Web lui fournit toujours des pages dans la mauvaise localisation.

Sous OS X, les environnements utilisateur ne sont pas démarrés par une pile de scripts shell, et launchd ne source le fichier .profile à aucun moment. (C'est en quelque sorte une honte, car cela signifie que définir des variables d'environnement est bien plus ennuyeux, mais telle est la vie.) Puisqu'il ne le ferait pas, quand est-ce qu'il fonctionnerait .profile? Seulement si tu es entré? Cela semble un peu inutile, car de nombreuses boîtes ne seront jamais la cible de ssh. Autrement, les terminaux doivent exécuter des shells de connexion par défaut, afin que .profile soit exécuté de temps en temps.

Qu'en est-il de .bashrc, alors? Est-ce inutile? Non, il a toujours le but qu'il avait à l'époque du VT100. Il est utilisé chaque fois que vous ouvrez un shell autre que l'ouverture d'une fenêtre de terminal. Donc, si vous utilisez Emacs ou vi, ou si vous faites un utilisateur su.

Alan Shutko
la source
1
La page Debian que vous citez explique pourquoi les .profilesources de Debian .bashrcet non pas pourquoi OSX a décidé de faire en sorte que tous les shells se connectent par shells. En fait, vous répondez à une autre question , et merci! Cependant, votre réponse n’explique pas le choix OSX et la citation Debian n’a absolument aucune pertinence (dans la mesure où je peux en dire plus (laissez-moi savoir si je manque le point). La manière OSX rend .bashrcetc inutile et ne rend .profilepas plus utile.
terdon
4

Je ne sais pas pourquoi ils l'auraient fait. Cependant, voici ma conjecture.

Pour commencer, il convient de noter que sur un système GNU / Linux, vous pouvez bien entendu passer à vt1, vt2, etc. Vous obtenez un shell de connexion à cet emplacement. Sur un système OS X, il n'y a pas d'équivalent. La seule façon d'accéder aux bases UNIX consiste à utiliser un émulateur de terminal ou un mode utilisateur unique (avertissement: je n'ai jamais utilisé le mode utilisateur unique; il s'agit d'un IIRC piloté par ligne de commande, mais je me trompe peut-être). Par conséquent, sous OS X, quelle que soit la valeur par défaut dans l'émulateur, il s'agit de la valeur par défaut pour l'ensemble du système.

Maintenant, pourquoi voudriez-vous que la valeur par défaut soit un shell de connexion? Il y a quelques raisons (lire: pas beaucoup) pour lesquelles je peux penser à faire cela.

  • Il fournit une expérience utilisateur cohérente si vous SSH dans la boîte. (Particulièrement important pour l'édition serveur d'OS X - sans doute si vous utilisez un serveur OS X, vous êtes un novice.)
  • Le shell par défaut d’OS X était tcsh. C’est à peu près la plus approximative possible, mais il se peut que tcshnormalement, quelque chose se passe lorsqu’il est exécuté en tant que shell de connexion et que le modèle historique reste bloqué. (J'en doute, cependant - peut-être qu'un des habitués les plus âgés pourrait nous le dire.)
  • "Nous sommes Apple. Nous sommes les vendeurs de la plus grande distribution UNIX sur la planète. Nos raisons sont sans importance. Si nous prenons une décision, votre outil doit en tenir compte."

Honnêtement, j'utilise Darwin depuis environ 6 ans et je ne peux pas répondre correctement à cette question. Cela n'a pas vraiment de sens pour moi non plus.

Pour répondre à votre sous-question, bashn’est pas corrigé ou quoi que ce soit (du moins pour cela). L'émulateur de terminal par défaut exécute des shells de connexion par défaut, et probablement iTerm le copie.

combattant
la source
1
+1 pour mentionner tcsh, car son comportement est bien plus sain à cet égard que celui de bash: lorsqu'il est lancé en tant que shell de connexion interactif, tcsh se base à la fois .profile et .tcshrc/ .cshrcsans nécessiter de kluges comme celui que j'ai donné dans ma réponse. Compte tenu de cela, je soupçonne que ce comportement est une régression non corrigée causée par le passage de tcsh à bash.
Ilmari Karonen
@IlmariKaronen Voulez-vous dire (ici et ) que les sources de tcsh .loginet .tcshrc/ .cshrc? Cela n'aurait aucun sens pour tcsh de se procurer .profile; il contient généralement des commandes avec une shsyntaxe qui tcshne sera pas acceptée. Je n'ai pas macOS mais j'ai créé /bin/tcshmon shell de connexion sous Ubuntu 16.04. .profilene provient pas. tcsh (1) ne mentionne pas ce fichier, pas plus que tcsh (1) .
Eliah Kagan
3

Ceci est une mise à jour du statut actuel: les réponses sont devenues obsolètes, de nouvelles versions de MacOSX ont été publiées et le comportement de connexion a changé.

Cette question a été posée et répondue en 2014. J'ai étudié le sujet pour tenter de créer un ensemble commun .bashrc & .bash_profile à utiliser sur différentes distributions Linux et BSD (comme ils les appellent, s'ils ne sont pas des distributions).

J'ai récemment acheté un Mac Mini usagé sur lequel Sierra est installé. J'ai donc maintenant des systèmes avec les versions 10.6, 10.10, 10.11 et 10.12. Bien que j'aie muni les plus anciens (en laissant des indices sur leur statut antérieur), l'installation de la version 10.12 de Sierra est intacte.

Résultats: Dans la version 10.12 de Sierra, AUCUN fichier .bashrc, .bash_profile ou .profile n'a été créé par défaut. Dans / etc, il y a bashrc, bashrc_Apple_Terminal et profile. Contenu de / etc / profile:

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

Contenu de / etc / bashrc:

# System-wide .bashrc file for interactive bash(1) shells.
if [ -z "$PS1" ]; then
   return
fi

PS1='\h:\W \u\$ '
# Make bash check its window size after a process completes
shopt -s checkwinsize

[ -r "/etc/bashrc_$TERM_PROGRAM" ] && . "/etc/bashrc_$TERM_PROGRAM"

Le script / etc / bashrc_Apple_Terminal définit la variable PROMPT_COMMAND et définit un mécanisme permettant de préserver l’état de la session pour chaque terminal; si l'application de terminal est fermée, l'état de la session précédente est restauré lorsque l'application est redémarrée. Sinon, presque rien ($ PS1 minimal) n'est défini dans / etc / bashrc, si bien que toute autre personnalisation (vraie $ PS1, alias, fonctions) sera définie dans les paramètres ~ / .bashrc et $ PATH dans .bash_profile (ou dans .profile , provenant de .bash_profile).

J'ai confirmé que iTerm2 se comporte de la même manière que l'application Terminal, à l'exception du fait que le comportement par défaut du démarrage d'une session de connexion est visible et peut être modifié.

Si vous exécutez une session de terminal XTerm X11 (maintenant XQuartz), vous verrez une session sans connexion, identique à celle d'un système Linux. .bash_profile (ou .profile) est ignoré et vous obtenez simplement .bashrc.

Et voici ma réponse:

Bien que l'application Terminal prétende être un xterm (TERM = xterm-256color), elle ne définit pas la variable $ DISPLAY sauf si X11 est installé. Nous assistons à une simulation d’un environnement X, mais pas tout à fait réelle. Si vous SSH dans un autre système avec le commutateur -X (activer le transfert X11), cela échouera car il n'y a pas de variable $ DISPLAY. Si vous avez installé X11, puis SSH sur un autre système, le transfert X11 aboutira.

Conclusion (et réponse courte): l'application Terminal n'est pas un véritable terminal X11 (ne définit pas $ DISPLAY); il se comporte beaucoup plus comme une connexion SSH qu'une session XTerm, en ce sens qu'il doit s'agir d'une session de connexion afin de définir les valeurs à partir de / etc / profile et ~ / .bash_profile ou ~ / .profile

donls
la source
0

Les réponses ci - dessus expliquent la raison pour laquelle les shells interactifs sont des coquilles de connexion sur macOS par défaut: paramètres dans /etc/profile, ~/.profilesont hérités par un obus non-connexion sur les systèmes basés sur X, mais pas sur macOS . Ici, je voudrais vous rappeler que vous devez toujours utiliser le shell de connexion sur macOS en raison de l'existence de path_helper:

 cat /etc/profile # or cat /etc/zprofile
# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

L' path_helperutilitaire lit le contenu des fichiers dans les répertoires /etc/paths.det /etc/manpaths.d et leur contenu ajoute les PATHet MANPATHvariables d' environnement respectivement. (La MANPATHvariable d'environnement ne sera modifiée que si elle est déjà définie dans l'environnement.)

Si vous utilisez un shell autre que la connexion, certains chemins ne seront pas importés.

Je pense que c'est toujours une bonne idée pour les développeurs de mettre un fichier dans /etc/paths.dpour importer leurs valeurs de chemin, mais pas pour créer un lien symbolique entre les fichiers binaires appelables et les emplacements tels que /usr/binou /bin. (La valeur par défaut PATHest /usr/bin:/bin:/usr/sbin:/sbin. Tout le monde n'utilise pas Homebrew ou MacPorts pour ajouter des valeurs personnalisées PATH. Il n'y a donc pas toujours de lieu sûr pour placer les liens symboliques des commandes appelables.)

Voici un exemple de fichiers dans /etc/paths.d. Fondamentalement, vous mettez une valeur à chaque ligne.

 cat /etc/paths.d/Wireshark
/Applications/Wireshark.app/Contents/MacOS

Référence:

Simba
la source