Objectif de [-n “$ PS1”] dans bashrc

10

À quoi sert l' [ -n "$PS1" ]in dans [ -n "$PS1" ] && source ~/.bash_profile;? Cette ligne est incluse dans un repo.bashrc d'un fichier dot .

shotes
la source

Réponses:

20

Ceci vérifie si le shell est interactif ou non. Dans ce cas, ne rechercher le ~/.bash_profilefichier que si le shell est interactif.

Voir "Est-ce que Shell est interactif?" dans le manuel bash, qui cite cet idiome spécifique. (Il recommande également de vérifier si le shell est interactif en testant si la $-variable spéciale contient le icaractère, ce qui est une meilleure approche de ce problème.)

filbranden
la source
bash, au moins, désactive PS1 et PS2 si le shell est interactif . Vous pouvez le voir par vous-même, avec ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), qui s'imprime simplement []. Il semble que zsh ne fasse pas de même, du moins à partir d'une expérience ... En tout cas, l' intention de la [ -n "$PS1" ]est de vérifier si le shell est interactif ou non.
filbranden
3
Cela bashdésactive PS1 lorsque non interactif (faute de frappe dans votre commentaire précédent) est un bug IMO, PS1 n'est pas une variable spécifique à bash, il n'a aucun intérêt à le désinstaller. C'est le seul shell qui le fait (bien qu'il soit yashégalement défini PS1sur une valeur par défaut même lorsqu'il n'est pas interactif).
Stéphane Chazelas
1
Étant donné que le code en question se trouve dans un fichier spécifique à bash, cela semble être une réponse raisonnable. D'autres réponses concernent le cas plus général des spécifications POSIX ou d'autres shells. Vous avez répondu "quel est le but de cela?" intention dans la Question tout en laissant de côté le reste. Il est bon de savoir ce que bash fait et peut-être aussi de meilleures façons d'atteindre l'objectif.
Jeff Schaller
Je considérerais cette réponse plus complète si elle suggérait une alternative plus fiable (disons, [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy
@CharlesDuffy Franchement, je ne pense pas qu'il y ait beaucoup de problèmes [ -n "${PS1}" ], mais j'ai toujours mis à jour ma réponse pour souligner que le manuel bash suggère / recommande également une inspection $-pour déterminer si le shell est interactif, j'espère que vous trouverez que cela améliore la réponse. À votre santé!
filbranden
19

Qu'est-ce que cela fait

Il s'agit d'un moyen répandu de tester si le shell est interactif. Attention, cela ne fonctionne qu'en bash, cela ne fonctionne pas avec d'autres shells. Donc, c'est ok (si idiot) pour .bashrc, mais cela ne fonctionnerait pas dans .profile(qui est lu par sh, et bash n'est qu'une des implémentations possibles de sh, et pas la plus courante).

Pourquoi ça marche (en bash seulement!)

Un shell interactif définit la variable shellPS1 sur la chaîne d'invite par défaut. Donc, si le shell est interactif, il PS1est défini (à moins que l'utilisateur ne l' .bashrcait supprimé, ce qui ne peut pas encore se produire au sommet de .bashrc, et vous pourriez considérer que c'est une chose idiote à faire de toute façon).

L'inverse est vrai dans bash: les instances non interactives de bash ne sont pas définies PS1au démarrage. Notez que ce comportement est spécifique à bash, et est sans doute un bug (pourquoi ne bash -c '… do stuff with $var…'fonctionne pas quand varest PS1?). Mais toutes les versions de bash jusqu'à 4.4 (y compris la dernière version que j'écris) le font.

De nombreux systèmes exportent PS1vers l'environnement. C'est une mauvaise idée, car de nombreux shells différents utilisent PS1mais avec une syntaxe différente (par exemple , les échappements d'invite de bash sont complètement différents des échappements d'invite de zsh ). Mais il est suffisamment répandu pour que dans la pratique, le PS1fait de le définir ne soit pas un indicateur fiable que le shell est interactif. Le shell peut avoir hérité PS1de l'environnement.

Pourquoi il est (mal) utilisé ici

.bashrcest le fichier que bash lit au démarrage lorsqu'il est interactif. Un fait moins connu est que bash lit également .bashrcun shell de connexion et l'heuristique de bash conclut qu'il s'agit d'une session distante (bash vérifie si son parent est rshdou sshd). Dans ce deuxième cas, il est peu probable que PS1cela soit défini dans l'environnement, car aucun fichier point n'a encore été exécuté.

Cependant, la façon dont le code utilise ces informations est contre-productive.

  • Si le shell est un shell interactif, cela s'exécute .bash_profiledans ce shell. Mais .bash_profilec'est un script au moment de la connexion. Il peut exécuter certains programmes destinés à être exécutés une seule fois par session. Il peut remplacer certaines variables d'environnement que l'utilisateur a délibérément définies sur une valeur différente avant d'exécuter ce shell. L'exécution .bash_profiledans un shell sans connexion est perturbatrice.
  • Si le shell est un shell de connexion à distance non interactif, il ne se charge pas .bash_profile. Mais c'est le cas où le chargement .bash_profilepourrait être utile, car un shell de connexion non interactif ne se charge pas automatiquement /etc/profileet ~/.profile.

Je pense que la raison pour laquelle les gens font cela est pour les utilisateurs qui se connectent via une interface graphique (un cas très courant) et qui mettent leurs paramètres de variable d'environnement .bash_profileplutôt que .profile. La plupart des mécanismes de connexion à l'interface graphique invoquent .profilemais pas .bash_profile(la lecture .bash_profilenécessiterait l'exécution de bash dans le cadre du démarrage de la session, au lieu de sh). Avec cette configuration, lorsque l'utilisateur ouvre un terminal, il obtient ses variables d'environnement. Cependant, l'utilisateur n'obtiendra pas ses variables d'environnement dans les applications GUI, ce qui est une source de confusion très courante. La solution ici consiste à utiliser .profileau lieu de .bash_profiledéfinir des variables d'environnement. L'ajout d'un pont entre .bashrcet .bash_profilecrée plus de problèmes qu'il n'en résout.

Que faire à la place

Il existe un moyen simple et portable de tester si le shell actuel est interactif: testez si l'option -iest activée.

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Ceci est utile .bashrcpour lire .profileuniquement si le shell n'est pas interactif - c'est -dire l'opposé de ce que fait le code! Lisez .profilesi bash est un shell de connexion (non interactif) et ne le lisez pas s'il s'agit d'un shell interactif.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles 'SO- arrête d'être méchant'
la source
4
Il vaut peut-être la peine de noter qu'une meilleure façon de tester si un shell est interactif est avec [[ -o interactive ]](ksh, bash, zsh) ou case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Mon bash (version 4.4.12) semble en fait ne pas être défini PS1s'il n'est pas exécuté de manière interactive. Il est assez facile à tester: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'n'imprimera rien, tandis que PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'la valeur $PS1définie dans vos fichiers de démarrage bash sera imprimée (il n'imprimera pas la chaîne "coucou").
FooF
1
@ Stéphane Chazelas: POSIX ne nécessite pas que $-contient iavec un shell interactif.
schily
1
Bosh fait cela depuis 2012 pour être compatible avec ksh. Il n'était tout simplement pas requis par POSIX jusqu'à ce que le changement que vous produisez devienne effectif.
schily
1
Franchement, je dirais que [ -n "${PS1}" ] mal appeler va un peu trop loin, après tout, cela ne casse que lorsque quelqu'un exporte PS1 (ce qui dans votre réponse vous dit que c'est une mauvaise idée et même expliquer les raisons) et cela n'affecte pas bash de toute façon (car il désactive PS1 et PS2 si le shell n'est pas interactif.) Peut-être aurait-il été préférable d'utiliser un mot tel que "découragé" ou de parler des "limites" de l'approche. Je ne pense pas que ce soit "faux" tout à fait. Si quelque chose ne va pas lors de l' exportation de PS1, c'est sûr! Quoi qu'il en soit, merci d'être entré dans les détails de cela.
filbranden
1

Il semble que ce concept étrange résulte du fait qu'il bashn'a pas commencé en tant que clone shell POSIX mais en tant que Bourne Shellclone.

Par conséquent, le comportement interactif POSIX ( $ENVappelé pour les shells interactifs) a été ajouté ultérieurement bashet n'est pas largement connu.

Il existe un shell qui accorde un comportement similaire. Il s'agit des cshsubventions csh qui $promptont des valeurs spécifiques:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Mais cela ne s'applique ni au Bourne Shell ni aux shells POSIX.

Pour un shell POSIX, la seule méthode autorisée consiste à mettre du code pour les shells interactifs dans le fichier:

$ENV

qui a un nom spécifique au shell. C'est par exemple

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

D'autres personnes ont mentionné le drapeau shell -i, mais ce n'est pas utilisable pour une programmation fiable. POSIX n'exige pas que cela set -ifonctionne, ni qu'il $-contienne un ipour les shells interactifs. POSIX requiert simplement que sh -ile shell soit appliqué en mode interactif.

Comme la variable $PS1peut être importée de l'environnement, elle peut avoir une valeur même en mode non interactif. Le fait que bash unsets PS1dans tout shell non interactif ne soit pas accordé par la norme et ne soit effectué par aucun autre shell.

Une programmation si propre (même avec bash) consiste à placer les commandes des shells interactifs dans $HOME/.bashrc.

schily
la source
0

Je vais d'abord parler de ce que Debian, et la plupart du temps aussi Ubuntu définit pour bash. Et ce dernier touche à d'autres systèmes.

Dans le cadre des fichiers de démarrage du shell, il y a beaucoup d'opinion.
J'ai également mon opinion mais je vais essayer de montrer des exemples existants de paramètres corrects.
J'utiliserai debuan car il est assez facile de trouver des exemples de ses fichiers.
Et debian est très utilisé, donc les paramètres ont été bien testés,

Quel est l'objectif de vérifier que la PS1 est définie?

Seulement pour savoir si le shell est interactif.

La valeur /etc/profilepar défaut dans debian et ubuntu (depuis / usr / share / base-files / profile):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

L'if est lu: s'il est interactif (ensemble par défaut PS1) et qu'il s'agit d'un shell bash (mais n'agissant pas par défaut sh), changez PS1 en un nouveau particulier (pas celui par défaut).

La valeur /etc/bash.bashrcpar défaut dans debian contient également:

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

Ce qui est assez clair dans ce qu'il fait: si interactif ne source pas (le reste).

Cependant, dans /etc/skel/.bashrcest un exemple de la bonne façon de tester un shell interactif (en utilisant $-):

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

Cela devrait montrer clairement pourquoi la PS1 et une alternative.

L'ordre correct

Le paramètre que vous signalez doit être évité.
L'ordre ( à partir des paramètres du système aux paramètres utilisateur plus spécifiques (pour bash)) est /etc/profile, /etc/bash.bashrc, ~/.profileet enfin ~/.bashrc. Cela place les effets les plus larges (et pour plus de shells) dans /etc/profile(qui appartient à root) suivi par /etc/bash.bashrc(qui appartient également à root) mais n'affecte que bash. Ensuite viennent les paramètres personnels $HOME, le premier est ~/.profilepour la plupart des shells et ~/.bashrc(presque équivalent à ~/.bash_profile), spécifique pour bash uniquement.

Il est donc erroné de la source ~/.bashrcdans ~/.profile, il est en train de transformer un utilisateur spécifique pour la mise en bash à un plus général qui touche de plus coquilles . Sauf si cela se fait de cette manière :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Il vérifie que bash est en cours d'exécution et ne se charge .bashrcque si c'est le cas.

Il s'agit d'une décision en amont venant de Debian. La justification est expliquée ici .

En fait, l'inverse, l'approvisionnement ~/.profileen ~/.bash_profile(ou ~/.bashrc) ne fait que réappliquer des règles générales qui auraient déjà dû être chargées dans un cas d'utilisation particulier, et donc "pas si mal" (je ne dis pas "bien"). Et je ne dis pas bien car cela peut provoquer une boucle de sourcing des fichiers. Comme lorsqu'un sous-répertoire charge un parent, c'est une boucle de répertoire.

Et c'est dans ce cross sourcing que le contrôle du shell interactif est logique. Ce n'est que lorsqu'un shell interactif est ~/.bashrcchargé, mais il peut à son tour se charger ~/.profile(ou l'inverse) et c'est dans ce cas que la recherche d'un shell interactif peut être utilisée.

Isaac
la source