Quel est l'équivalent ZSH de $ PROMPT_COMMAND de BASH?

24

BASH prend en charge une $PROMPT_COMMANDvariable d'environnement qui définit une commande à exécuter avant toute invite interactive de premier niveau. Je recherche un équivalent ZSH de cela.

La documentation dit qu'il y a une fonction que precmdje peux définir pour y parvenir; cependant, je ne sais pas comment le définir à partir d'une variable d'environnement.

J'ai envisagé de passer une variable d'environnement qui obligerait ZSH à lire un fichier contenant la définition de cette fonction, mais ZSH ne semble pas prendre en charge de telles choses : il ne lit que les fichiers globaux, puis les fichiers par utilisateur. Je peux les remplacer mais je ne peux pas y ajouter sans modifier les fichiers, ce que je ne peux pas faire.

Alors, comment puis-je définir un hook pré-invite dans ZSH via une variable d'environnement, comme je le ferais $PROMPT_COMMANDen BASH?

Shnatsel
la source
À vrai dire, j'ai besoin d'un hook post-interactif de commande-exécution, mais aucun shell n'en fournit un donc je dois recourir à des hooks pré-invite - ils semblent être aussi proches que possible.
Shnatsel
1
Hm, je me demande quelle est la différence entre l' exécution d'une commande post-interactive et la pré-invite . En dehors d'une différence conceptuelle, où observez-vous réellement une différence. (Omettons les commandes exitet exec, ok ;))
mpy
@mpy il y a une différence lors de l'exécution d'un travail en arrière-plan, car les travaux en arrière-plan sont indépendants de la séquence d'invites.
Shnatsel
1
Ok, j'ai compris ce point. Alors, que diriez-vous de quelque chose comme ça: start() { eval "$@"; echo post-command-code }puis utilisez une liaison zle pour exécuter la ligne de commande avec startpréfixé?
mpy
1
Le DEBUGpiège est une bonne trouvaille, mais vous avez toujours du mal à le définir. J'ai étendu ma réponse une fois de plus, mais je vous laisse écrire votre propre réponse concernant la solution de piège DEBUG. :)
mpy

Réponses:

24

L'approche la plus simple pour émuler les bash $PROMPT_COMMANDqui me vient à l'esprit est d'utiliser le precmdcrochet, comme vous l'avez déjà compris. Définissez-le comme

precmd() { eval "$PROMPT_COMMAND" }

et vous pouvez faire quelque chose comme ça:

$ PROMPT_COMMAND='echo Hello, it is now $(date)'
Hello, it is now Mon, Mar 31, 2014 7:08:00 PM
$ whoami      
user
Hello, it is now Mon, Mar 31, 2014 7:08:21 PM     
$

Veuillez noter les guillemets simples dans cet exemple, sinon ils $(date)seront développés trop tôt, c'est-à-dire déjà lors de la définition $PROMPT_COMMANDet non lorsqu'ils seront appelés avant l'invite.


Si vous souhaitez conserver (et ne souhaitez pas modifier) ​​la définition existante, vous pouvez utiliser cette approche:

$ prmptcmd() { eval "$PROMPT_COMMAND" }
$ precmd_functions=(prmptcmd)

Avec cela, les prmptcmdfonctions sont exécutées après la precmd()fonction existante .


Enfin, voici un moyen qui convient à une utilisation dans un package de programme, qui ne doit pas modifier les fichiers utilisateur ou système ni entrer les commandes de manière interactive.

Un exemple pour lancer une session bash pourrait être

PROMPT_COMMAND="echo foo" bash

Pour générer zsh, vous pouvez utiliser

ZDOTDIR=/program/dir zsh

ce qui fait /program/dir/.zshrcque la source. Dans ce fichier, le precmd()crochet peut être défini comme expliqué ci-dessus. Si vous souhaitez que les paramètres de l'utilisateur , en plus comprennent , source $HOME/.zshrcetc. dans le .zshrc aussi du programme. Cette configuration est maintenable, car aucun fichier en dehors du répertoire du programme n'est modifié.


Comme dernier ajout, voici une preuve de concept sur la façon de souhaiter la bienvenue au nouvel utilisateur. Utilisez le code suivant dans votre /program/dir/.zshenvfichier de configuration rc:

echo define precmd, traps, etc.

autoload -Uz zsh-newuser-install

if [[ ! -e "$HOME/.zshrc" ]]; then
  zsh-newuser-install -f
  mv $ZDOTDIR/.zshrc $HOME/.zshrc
else
  builtin source $HOME/.zshrc
fi
mpy
la source
C'est ce que je pensais. Le problème est - comment définir le hook precmd via une variable d'environnement? Existe-t-il un mécanisme pour ajouter des hooks ou du code sans modifier les fichiers? Ou comment puis-je le faire au moins sans écrire dans les fichiers ".zprofile" global et utilisateur global et similaires? Par exemple, puis-je ajouter mon propre fichier .zprofile qui ne remplacera pas les fichiers existants?
Shnatsel
1
De plus, votre utilisation du crochet precmd ici remplacerait tout crochet precmd existant déjà; Les documents zsh mentionnent que je peux créer un tableau de fonctions qui coexisteront, mais je n'ai aucune idée de comment le faire.
Shnatsel
1
(1) Que voulez-vous dire par comment définir le hook precmd via une variable d'environnement? L'exemple que j'ai présenté fonctionne à mon humble avis comme le mécanisme bash. (2) Vous pouvez ajouter le hook via la ligne de commande, mais ce n'est pas permanent. Quel est le problème avec la modification de votre .zshrc? (3) Un exemple: foo() { echo foo }; bar() { echo bar }; precmd_functions=(foo bar)Ceci s'exécute foo()et bar() en plus de precmd().
mpy
2
Ok, cela clarifie beaucoup - un exemple minimal pour bash serait alors PROMPT_COMMAND="echo foo" bash, non? Est - ce possible d'un pour le frai zsh: ZDOTDIR=/program/dir zsh. Puis /program/dir/.zshrcest originaire au démarrage où vous pouvez définir le hook precmd (). Si vous voulez que l'utilisateur inclue en plus source $HOME/.zshrcetc. dans le zshrc du programme. Cela devrait être facile à maintenir, car aucun fichier en dehors du répertoire du programme n'est modifié.
mpy
1
@Shnatsel: J'ai étendu ma réponse. Vous pouvez peut-être également modifier votre question pour inclure les informations supplémentaires de vos commentaires.
mpy
5

Comme l'indique @mypy, Zsh precmdfonctionne de manière similaire à Bash PROMPT_COMMAND.

Voici un exemple qui fonctionne pour Bash ou Zsh et n'utilise pas eval:

## ~/myprompt.sh

# 'ZSH_VERSION' only defined in Zsh
# 'precmd' is a special function name known to Zsh

[ ${ZSH_VERSION} ] && precmd() { myprompt; }

# 'BASH_VERSION' only defined in Bash
# 'PROMPT_COMMAND' is a special environment variable name known to Bash

[ ${BASH_VERSION} ] && PROMPT_COMMAND=myprompt

# function called every time shell is about to draw prompt
myprompt() {
  if [ ${ZSH_VERSION} ]; then
    # Zsh prompt expansion syntax
    PS1='%{%F{red}%}%n%{%f%}@%{%F{red}%}%m %{%F{cyan}%}%~ %{%F{white}%}%# %{%f%}'
  elif [ ${BASH_VERSION} ]; then
    # Bash prompt expansion syntax
    PS1='\[\e[31m\]\u\[\e[0m\]@\[\e[31m\]\h \[\e[36m\]\w \[\e[37m\]\$ \[\e[0m\]'
  fi
}

Exécuter à partir de scripts d'initialisation shell:

## ~/.bashrc
. ~/myprompt.sh

et:

## ~/.zshrc
. ~/myprompt.sh

Les invites ici ne sont que des exemples. On peut certainement faire des choses beaucoup plus délicates.

Pour plus de détails sur la définition des fonctions d'invite, voir: http://zsh.sourceforge.net/Doc/Release/Functions.html#index-precmd et http://www.gnu.org/software/bash/manual/bashref.html # Impression rapide .

Pour plus de détails sur les extensions rapides, voir http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html et http://www.gnu.org/software/bash/manual/bashref.html#Printing-a -Invite .

jwfearn
la source