Comment puis-je exécuter une commande dans bash après toute modification de $ PWD?

10

zsh fournit de belles fonctions de hook , y compris chpwdpour exécuter une fonction après que l'utilisateur a changé de répertoire.

# zsh only
function greet() { echo 'hi'; }
chpwd_functions+=("greet")
cd .. # hi
pushd # hi
popd  # hi

J'essaie d'imiter ça en bash.

Contraintes:

  • Il doit fonctionner à la fois dans des shells interactifs et non interactifs, ce qui signifie, je pense, qu'il ne peut pas s'appuyer sur quelque chose comme $PROMPT_COMMAND
  • Il ne peut pas être redéfini cd, car je veux qu'il fonctionne pour toute commande qui modifie les répertoires (par exemple, pushdet popd)
  • Il doit s'exécuter après la commande de l'utilisateur, donc trap "my_function" DEBUGcela ne fonctionne pas, à moins que je ne puisse dire en quelque sorte, "exécutez d'abord le $BASH_COMMANDnous avons piégé, puis faites aussi cela ..." Je vois que je peux éviter l'exécution automatique de $BASH_COMMAND si extdebug est activé et la fonction trap renvoie 1, mais je ne pense pas que je veux forcer extdebug, et le retour 1pour une commande réussie (mais modifiée) semble incorrect.

La dernière partie - "exécuter après la commande de l'utilisateur" - est ce qui me dérange actuellement. Si je peux exécuter une fonction après chaque commande, je peux la faire vérifier si le répertoire a changé depuis notre dernière vérification. Par exemple:

function check_pwd() {
  # true in a new shell (empty var) or after cd
  if [ "$LAST_CHECKED_DIR" != "$PWD" ]; then
    my_function
  fi
  LAST_CHECKED_DIR=$PWD
}

Suis-je sur la bonne voie ou existe-t-il une meilleure solution? Comment puis-je exécuter une commande dans bash après que l'utilisateur a changé de répertoire?

Nathan Long
la source
3
Pourquoi ne pas redéfinir cd, pushdet popd? Combien d'autres façons de changer de répertoire?
jw013
@ jw013 ce code est destiné à entrer dans un projet open source où le mainteneur a spécifiquement listé la non redéfinition cdcomme principe.
Nathan Long
Aussi - "combien d'autres façons de changer de répertoire" - je ne sais pas, c'est une autre raison pour laquelle je préférerais ne pas compter sur leur liste explicite.
Nathan Long
Pourquoi veux-tu faire cela? Votre fonction peut freiner de nombreux programmes (C, Java, ..). Dans les scripts que j'utilise, MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )veuillez ne pas modifier les commandes Unix fiables.
Walter A
1
@NathanLong, c'est uniquement pour mon système d'exploitation . Le système d'exploitation ne semble pas pertinent ici. L'OS est-il important? C'est le shell qui compte dans votre question, et vous semblez demander spécifiquement bashce qui fonctionne à peu près de la même manière sur tous les systèmes d'exploitation sur lesquels il fonctionne.
jw013

Réponses:

2

Il n'y a aucun moyen qui réponde à ces contraintes

Il semble qu'il n'y ait aucun moyen de résoudre ce problème en bash avec mes contraintes. En général, les solutions possibles sont:

  • Override cd, pushdet popd, de sorte que toute commande qui change les répertoires d' abord exécuter la fonction crochet. Mais cela peut créer des problèmes car 1) la substitution doit être prudente pour terminer la tabulation comme la commande d'origine et retourner le même code de sortie et 2) si plusieurs outils adoptent cette approche, ils ne peuvent pas bien jouer ensemble
  • Remplacez toutes les commandes qui peuvent être exécutées avec les modifications d'environnement pour exécuter d'abord la fonction de raccordement. C'est difficile car il existe de nombreuses commandes de ce type
  • trap 'my_functionDEBUG so that every command will run the hook function. This is suboptimal because 1) it runs before every command, 2) it runs *before*cd`, pas après 3) il ne peut y avoir qu'une seule fonction de débogage, donc si un autre outil utilise cette approche, ils ne peuvent pas bien jouer ensemble
  • Redéfinissez $PROMPT_COMMANDpour exécuter la fonction de crochet en premier. Ce n'est pas optimal car cela ne fonctionnera pas dans les shells non interactifs et parce que si un autre outil définit la commande d'invite, ils ne peuvent pas bien jouer ensemble.

En bref, il semble que la seule excellente solution serait que bash fournisse quelque chose comme le chpwd_functionscrochet de zshell , mais il semble impossible de le simuler correctement.

Nathan Long
la source
1

Si le responsable n'aime pas que vous changiez la définition de cd, une autre option serait de redéfinir toutes les commandes qui utilisent cet environnement dans le répertoire:

for c in cmd1 cmd2 cmd3 cmd4 cmd5 ; do
    eval "$c() { check_pwd ; command $c \"\$@\" ; }"
done

Cela serait assez efficace car le hook PWD et la configuration dans le répertoire ne seraient traités qu'en cas de besoin.

Dans votre exemple de fonction check_pwd, je pourrais changer:

my_function

à:

my_function "$PWD"

afin de passer dans le nouveau cwd (pourrait être plus modulaire, testable).

Gregor
la source
"redéfinir toutes les commandes qui utilisent cet environnement dans le répertoire" Malheureusement, je ne peux pas prédire cela.
Nathan Long
0

Installez les outils inotify en fonction de votre distribution.

inotifywait -emodify,create,delete -m /path/to/directory | while read line; do service httpd reload; done

Dans mon exemple, la commande suivante redémarrera httpdsi quelque chose change (Modifier, Créer, Supprimer) dans le répertoire spécifié.

Ali Pandidan
la source