Wie kann ich nach einer Änderung von $ PWD einen Befehl in bash ausführen?

10

zsh bietet einige nette Hook-Funktionen , einschließlich chpwdzum Ausführen einer Funktion, nachdem der Benutzer die Verzeichnisse gewechselt hat.

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

Ich versuche das in Bash zu emulieren.

Einschränkungen:

  • Es muss sowohl in interaktiven als auch in nicht interaktiven Shells funktionieren, was meiner Meinung nach bedeutet, dass es sich nicht auf so etwas verlassen kann $PROMPT_COMMAND
  • Es kann nicht neu definiert werden cd, da ich möchte, dass es für jeden Befehl funktioniert, der Verzeichnisse ändert (z. B. pushdund popd).
  • Es muss nach dem Befehl des Benutzers ausgeführt werden, trap "my_function" DEBUGfunktioniert also nicht, es sei denn, ich kann dort irgendwie sagen: "Führen Sie zuerst das, was $BASH_COMMANDwir gefangen haben, dann auch dies aus ..." Ich sehe, dass ich das automatische Ausführen von $BASH_COMMAND if vermeiden kann, wennextdebug es aktiviert ist und die Trap-Funktion gibt 1 zurück, aber ich glaube nicht, dass ich erzwingen möchte extdebug, und die Rückkehr 1für einen erfolgreichen (aber geänderten) Befehl scheint falsch.

Der letzte Teil - "Nach dem Befehl des Benutzers ausführen" - hat mich derzeit verblüfft. Wenn ich nach jedem Befehl eine Funktion ausführen kann , kann ich überprüfen lassen, ob sich das Verzeichnis seit unserer letzten Überprüfung geändert hat. Z.B:

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
}

Bin ich auf dem richtigen Weg oder gibt es einen besseren Weg? Wie kann ich einen Befehl in bash ausführen, nachdem der Benutzer die Verzeichnisse geändert hat?

Nathan Long
quelle
3
Warum neu definiert nicht cd, pushdund popd? Wie viele andere Möglichkeiten gibt es, das Verzeichnis zu wechseln?
jw013
@ jw013 Dieser Code soll in ein Open-Source-Projekt einfließen, in dem der Betreuer ausdrücklich aufgeführt hat, dass er nicht cdgrundsätzlich neu definiert.
Nathan Long
Außerdem - "wie viele andere Möglichkeiten gibt es, das Verzeichnis zu wechseln" - weiß ich nicht, was ein weiterer Grund ist, warum ich mich lieber nicht darauf verlassen möchte, sie explizit aufzulisten.
Nathan Long
Warum willst du das tun? Ihre Funktion kann viele Programme (C, Java, ..) bremsen. In Skripten, die ich verwende MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P ), ändern Sie bitte nicht die vertrauenswürdigen Unix-Befehle.
Walter A
1
@ NathanLong das ist nur für mein Betriebssystem . Das Betriebssystem scheint hier irrelevant. Ist das Betriebssystem wichtig? Es ist die Shell, die in Ihrer Frage wichtig ist, und Sie scheinen speziell danach zu fragen bash, was auf allen Betriebssystemen, auf denen es ausgeführt wird, ziemlich gleich funktioniert.
jw013

Antworten:

2

Es gibt keine Möglichkeit, diese Einschränkungen zu erfüllen

Es sieht so aus, als gäbe es keine Möglichkeit, dieses Problem mit meinen Einschränkungen zu lösen. Im Allgemeinen sind mögliche Lösungen:

  • Außer Kraft setzen cd, pushdund popd, so dass jeder Befehl, die Verzeichnisse ändert zuerst die Hook - Funktion ausgeführt werden . Dies kann jedoch zu Problemen führen, da 1) beim Überschreiben darauf geachtet werden muss, dass die Registerkarte wie beim ursprünglichen Befehl vollständig ausgefüllt wird und derselbe Exit-Code zurückgegeben wird, und 2) wenn mehr als ein Tool diesen Ansatz verfolgt, sie nicht gut zusammenspielen können
  • Überschreiben Sie alle Befehle, die möglicherweise mit den Umgebungsänderungen ausgeführt werden, um zuerst die Hook-Funktion auszuführen. Dies ist schwierig, da es viele solcher Befehle gibt
  • 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`, nicht nach 3) Es kann nur eine Debug-Funktion geben. Wenn also ein anderes Tool diesen Ansatz verwendet, können sie nicht gut zusammen spielen
  • Definieren Sie neu $PROMPT_COMMAND, um zuerst die Hook-Funktion auszuführen. Dies ist nicht optimal, da es in nicht interaktiven Shells nicht funktioniert und wenn ein anderes Tool den Eingabeaufforderungsbefehl definiert, können sie nicht gut zusammenspielen.

Kurz gesagt, es scheint, als wäre die einzige gute Lösung, wenn bash so etwas wie zshells chpwd_functionsHaken liefern würde , aber es scheint unmöglich, dies richtig zu simulieren.

Nathan Long
quelle
1

Wenn der Betreuer nicht möchte, dass Sie die Definition für CD ändern, können Sie auch alle Befehle neu definieren, die diese In-Directory-Umgebung verwenden:

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

Dies wäre sehr effizient, da der PWD-Hook und die In-Directory-Konfiguration nur bei Bedarf verarbeitet würden.

In Ihrer Beispielfunktion check_pwd könnte ich Folgendes ändern:

my_function

zu:

my_function "$PWD"

um das neue cwd zu übergeben (möglicherweise modularer, testbar).

Gregor
quelle
"Definieren Sie alle Befehle neu, die diese In-Directory-Umgebung verwenden" Leider kann ich das nicht vorhersagen.
Nathan Long
0

Installieren Sie inotify-tools entsprechend Ihrer Distribution.

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

In meinem Beispiel wird der folgende Befehl neu httpdgestartet, wenn sich im angegebenen Verzeichnis etwas ändert (Ändern, Erstellen, Löschen).

Ali Pandidan
quelle