Quelle est la différence entre PS1 et PROMPT_COMMAND

108

En jetant un œil à ce fil génial, j'ai remarqué que certains exemples utilisent

PS1="Blah Blah Blah"

et une certaine utilisation

PROMPT_COMMAND="Blah Blah Blah"

(et certains utilisent les deux) lors de la définition de l'invite dans un shell bash. Quelle est la différence entre les deux? Une recherche SO et même une recherche Google plus large ne me donnent pas de résultats, donc même un lien vers le bon endroit pour rechercher la réponse serait apprécié.

Jed Daniels
la source

Réponses:

59

Depuis la page de documentation GNU Bash: http://www.gnu.org/software/bash/manual/bashref.html

PROMPT_COMMAND
    If set, the value is interpreted as a command to execute before
    the printing of each primary prompt ($PS1).

Je ne l'ai jamais utilisé, mais j'aurais pu l'utiliser quand je n'avais que sh.

Scott Thomson
la source
67

PROMPT_COMMAND peut contenir des instructions bash ordinaires tandis que la variable PS1 peut également contenir des caractères spéciaux, tels que «\ h» pour le nom d'hôte, dans la variable.

Par exemple, voici mon invite bash qui utilise à la fois PROMPT_COMMAND et PS1. Le code bash dans PROMPT_COMMAND détermine dans quelle branche git vous pourriez être et l'affiche à l'invite, ainsi que l'état de sortie du dernier processus exécuté, le nom d'hôte et le nom de base du pwd. La variable RET stocke la valeur de retour du dernier programme exécuté. Ceci est pratique pour voir s'il y a eu une erreur et le code d'erreur du dernier programme que j'ai exécuté dans le terminal. Notez le «externe» entourant toute l'expression PROMPT_COMMAND. Il inclut PS1 afin que cette variable soit réévaluée à chaque fois que la variable PROMPT_COMMAND est évaluée.

PROMPT_COMMAND='RET=$?;\
  BRANCH="";\
  ERRMSG="";\
  if [[ $RET != 0 ]]; then\
    ERRMSG=" $RET";\
  fi;\
  if git branch &>/dev/null; then\
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2);\
  fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'

L'exemple de sortie ressemble à ceci dans un répertoire non-git:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 

et dans un répertoire git, vous voyez le nom de la branche:

sashan@dhcp-au-122 rework mybranch $ 

Mettre à jour

Après avoir lu les commentaires et la réponse de Bob, je pense qu'il est préférable de l'écrire comme il le décrit. C'est plus facile à maintenir que ce que j'ai écrit à l'origine ci-dessus, où la variable PS1 est définie dans PROMPT_COMMAND, qui est elle-même une chaîne super compliquée qui est évaluée au moment de l'exécution par bash. Cela fonctionne, mais c'est plus compliqué que nécessaire. Pour être honnête, j'ai écrit ce PROMPT_COMMAND pour moi-même il y a environ 10 ans et cela a fonctionné et n'y a pas trop pensé.

Pour ceux qui sont curieux de savoir comment j'ai modifié mes choses, j'ai essentiellement mis le code de PROMPT_COMMAND dans un fichier séparé (comme Bob l'a décrit), puis j'ai fait écho à la chaîne que j'ai l'intention d'être PS1:

GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour

if [ -z $SCHROOT_CHROOT_NAME ]; then
    SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
    ERRMSG=" $RET"
fi
if which git &>/dev/null; then
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2)
else
    BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"

et dans mon .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
sashang
la source
1
Vous pouvez raccourcir une de vos lignes: if git branch &>/dev/null ; then\ . Il redirige à la fois stdout et stderr vers / dev / null. tldp.org/LDP/abs/html/io-redirection.html
3
Il n'est pas nécessaire d' exporter PROMPT_COMMAND .
dolmen
2
Je pense que le commentaire de ceving est tout à fait vrai pour cette réponse aussi:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
Je ne vois pas de raison pour laquelle changer en PS1ligne à l'intérieur PROMPT_COMMANDest désavantageux. C'est un code utile parfait. Contrairement à la réponse de Bob vers le bas, la PS1variable a été correctement construite. Cela permet une invite de bash beaucoup plus sophistiquée en fonction de votre situation réelle.
Christian Wolf
2
La construction de l' PS1intérieur de @ChristianWolf PROMPT_COMMANDne sert à rien. c'est un exemple de comment ne pas le faire. construisez PS1une fois dedans .bash_profile, utilisez simplement des guillemets simples au lieu de guillemets doubles, de sorte que les substitutions de variables seront évaluées à chaque invite.
pal
46

La différence est que PS1 est la chaîne d'invite réelle utilisée et PROMPT_COMMAND est une commande qui est exécutée juste avant l'invite. Si vous voulez la manière la plus simple et la plus flexible de créer une invite, essayez ceci:

Mettez ceci dans votre .bashrc:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Ensuite, écrivez un script (bash, perl, ruby: votre choix), et placez-le dans ~ / bin / bash_prompt.

Le script peut utiliser n'importe quelle information pour créer une invite. C'est beaucoup plus simple IMO car vous n'avez pas à apprendre le langage de substitution un peu baroque qui a été développé uniquement pour la variable PS1.

Vous pourriez penser que vous pourriez faire de même en définissant simplement PROMPT_COMMAND directement sur ~ / bin / bash_prompt, et en définissant PS1 sur la chaîne vide. Au début, cela semble fonctionner, mais vous découvrez rapidement que le code readline s'attend à ce que PS1 soit défini sur l'invite réelle, et lorsque vous faites défiler les backwords dans l'historique, les choses se gâtent en conséquence. Cette solution de contournement oblige PS1 à toujours refléter la dernière invite (puisque la fonction définit la PS1 réelle utilisée par l'instance appelante du shell), ce qui permet à readline et à l'historique des commandes de fonctionner correctement.

Bob
la source
17
Ne pas mettre PS1en PROMPT_COMMAND! Définissez des variables dans PROMPT_COMMANDet utilisez-les dans PS1. Sinon, vous perdrez la possibilité d'utiliser les PS1séquences d'échappement comme \uou \h. Vous devez les réinventer PROMPT_COMMAND. Cela pourrait être possible, mais il n'est pas possible de contourner la perte de \[et \]qui marquent le début et la fin des caractères non imprimables. Cela signifie que vous ne pouvez pas utiliser de couleurs sans confondre le terminal sur la longueur de l'invite. Et cela crée de la confusion readlinelors de la modification d'une commande engendrant deux lignes. En fin de compte, vous avez un gros désordre sur l'écran.
ceving
1
@ceving C'est vrai! On peut utiliser PROMPT_COMMAND pour changer le format de votre PS1 et obtenir le meilleur des deux mondes
2grit
3
PROMPT_COMMANDest exécuté avant l'impression PS1. Je ne vois aucun problème de réglage PS1de l'intérieur PROMPT_COMMAND, car une fois PROMPT_COMMANDterminé, le shell imprimera PS1, qui a été modifié à partir de PROMPT_COMMAND(ou dans ce cas, à l'intérieur prompt_command)?
Felipe Alvarez
3
Attention: PROMPT_COMMAND ne doit généralement pas être utilisé pour imprimer des caractères directement dans l'invite. Les caractères imprimés en dehors de PS1 ne sont pas comptés par Bash, ce qui l'amènera à placer incorrectement le curseur et à effacer les caractères. Utilisez PROMPT_COMMAND pour définir PS1 ou regardez les commandes d'intégration. ( Arch Wiki Source )
meffect
3
Je ne comprends pas pourquoi tout le monde essaie de faire des astuces dans PROMPT_COMMAND au lieu d'utiliser simplement la substitution de commandes dans PS1 export PS1='$(~/bin/bash_prompt)'fait la même chose que le bogue semble sain
pal
10

De man bash:

PROMPT_COMMAND

S'il est défini, la valeur est exécutée en tant que commande avant d'émettre chaque invite principale.

PS1

La valeur de ce paramètre est développée (voir PROMPTING ci-dessous) et utilisée comme chaîne d'invite principale. La valeur par défaut est '' \ s- \ v \ $ ''.

Si vous souhaitez simplement définir la chaîne d'invite, l'utilisation PS1seule suffit:

PS1='user \u on host \h$ '

Si vous voulez faire autre chose juste avant d'imprimer l'invite, utilisez PROMPT_COMMAND. Par exemple, si vous souhaitez synchroniser les écritures mises en cache sur le disque, vous pouvez écrire:

PROMPT_COMMAND='sync'
Cyker
la source
1
Vous pouvez également définir le titre du terminal à partir de PS1sans avoir besoin PROMPT_COMMAND, car la séquence qui définit le titre peut être incluse dans PS1enveloppé avec \[et \].
dolmen
1
@dolmen Très bien. Ensuite, faisons autre chose, comme définir dynamiquement une variable d'environnement.
Cyker
@Cyker vous pouvez définir dynamiquement la variable d'environnement dans PS1, elle sera simplement définie dans le sous-shell, vous ne pouvez donc pas récupérer sa valeur. mais votre exemple est trivialPS1='$(sync)user \u on host \h$ '
pal
1

la différence est que

  • si vous sortez une ligne incomplète à partir de PROMPT_COMMAND, votre invite bash sera vissée
  • PS1substituts \Het amis
  • PROMPT_COMMANDexécute son contenu, PS1utilise son contenu comme invite.

PS1fait l'expansion de variable et la substitution de commande à chaque invite, pas besoin d'utiliser PROMPT_COMMANDpour attribuer une valeur PS1ou pour exécuter du code arbitraire. vous pouvez facilement le faire export PS1='$(uuidgen) $RANDOM'une fois .bash_profile, utilisez simplement des guillemets simples

copain
la source
0

Ouais, alors pour essayer de vraiment comprendre ça:

  • PROMPT_COMMANDest une variable / fonction pratique de bash , mais il n'y a, à proprement parler, rien qui ne puisse être fait en utilisant PS1seul, n'est-ce pas ?

Je veux dire, si l'on veut définir une autre variable avec une portée en dehors de l'invite: selon le shell, cette variable devrait probablement être déclarée en premier à l'extérieur $PS1ou (dans le pire des cas), on pourrait avoir envie de quelque chose en attente sur un FIFO avant appel $PS1(et armé à nouveau à la fin de $PS1); cela \u \hpourrait causer des problèmes, en particulier si vous utilisez une expression régulière sophistiquée; mais sinon: on peut accomplir tout ce que l' PROMPT_COMMANDon peut en utilisant la substitution de commandes à l'intérieur $PS1(et, peut-être dans les cas de coin, des sous-shell explicites)?

Droite?

Geoff Nixon
la source