So führen Sie den Shell-Befehl mithilfe des Shell-Profils und der aktuellen Verzeichnis-Hooks aus (z. B. direnv)


9

Ich versuche , zu erhalten shell-commandund async-shell-commandmit ein paar Programme in meiner nahtlos zu integrieren .bashrcDatei, speziell direnv in diesem Beispiel.

Ich stellte fest, dass ich beim Anpassen shell-command-switchShell-Prozesse dazu bringen könnte, mein Profil so zu laden, als wäre es eine reguläre interaktive Login-Shell:

(setq shell-command-switch (purecopy "-ic"))
(setq explizit-bash-args '("-ic" "export EMACS =; stty echo; bash"))

Ich benutze auch exec-path-from-shell .

Angenommen, ich habe eine ~/.bashrcDatei mit:

...

eval "$ (direnv hook $ 0)"
Echo "foo"

Im Inneren habe ~/code/fooich eine .envrcDatei mit:

export PATH = $ PWD / bin: $ PATH
Echo "Bar"

Wenn ich M-x shellmit default-directoryset to ~/code/fooausführe, lädt eine Bash-Shell mein Profil korrekt und führt den direnv-Hook aus, um dies meinem Pfad hinzuzufügen:

direnv: Laden von .envrc
Bar
direnv: export ~ PATH
~ / code / foo $ echo $ PATH
/ Benutzer / Benutzername / Code / foo / bin: / usr / local / bin: ... # Rest von $ PATH

Wenn dies default-directoryjedoch immer noch der Fall ist ~/code/foound ich ausgeführt werde M-! echo $PATH, wird meine .bashrc-Datei korrekt geladen, der direnv-Hook des aktuellen Verzeichnisses wird jedoch nicht ausgeführt:

foo
/ usr / local / bin: ... # Rest von $ PATH ohne ./bin

Ich bekomme das gleiche Ergebnis, wenn ich renne M-! cd ~/code/foo && echo $PATH.

Gibt es eine Weise , die ich beraten oder Haken in shell-commandoder start-processum es sich verhalten , als ob es aus einem interaktiven Shell - Puffer gesendet wurden?


Wie sieht dein direnv hook aus? Wenn es in ~ / .bashrc definiert ist und Sie (setq shell-command-switch "-ic")es ausgewertet haben, sollte es zusammen mit jedem anderen Befehl in ~ / .bashrc ausgewertet werden.
— rekado

Die Zeile in meinem ~ / .bashrc ist eval "$(direnv hook $0)". Dies wird ausgeführt, aber der Mechanismus, der ausgelöst werden soll, wenn Sie sich in einem bestimmten Verzeichnis mit einer .envrcDatei befinden, ist dies nicht.
— Waymondo

Wird nichts in der .envrcDatei ausgewertet? Oder werden nur Umgebungsvariablen nicht exportiert? Könnten Sie bitte ein vollständiges Beispiel angeben, damit ich versuchen kann, dies zu reproduzieren?
— rekado

@rekado - Danke, dass du es dir angesehen hast. Ich habe meine Frage so aktualisiert, dass sie für die Reproduktion expliziter ist.
— Waymondo

Antworten:


5

Dies scheint kein Problem mit Emacs zu sein, sondern mit Bash. shell-commandwird nur call-processauf der Shell ausgeführt und übergibt Argumente. Ich habe das auf einer normalen Shell versucht:

bash -ic "cd ~/code/foo && echo $PATH"

~/.bashrcwird bezogen, aber der direnv-Hook wird nicht ausgeführt. Wenn direnv hook bashausgeführt, wird eine Funktion _direnv_hookausgegeben und vorangestellt PROMPT_COMMAND.

_direnv_hook() {
  eval "$(direnv export bash)";
};
if ! [[ "$PROMPT_COMMAND" =~ _direnv_hook ]]; then
  PROMPT_COMMAND="_direnv_hook;$PROMPT_COMMAND";
fi

Ich vermute, das wird PROMPT_COMMANDeinfach nicht funktionieren. Das ist aber kein Problem, denn das _direnv_hookist wirklich einfach. Wir können einfach eval $(direnv export bash)den Shell-Befehl voranstellen und es wird funktionieren:

(let ((default-directory "~/code/foo/"))
  (shell-command "eval \"$(direnv export bash)\" && echo $PATH"))

Dadurch wird der erweiterte Pfad zum Nachrichtenpuffer gedruckt. Alternativ können Sie Folgendes ausführen M-!:

cd ~/code/foo && eval "$(direnv export bash)" && echo $PATH

Sie müssen nicht, (setq shell-command-switch "-ic")damit dies funktioniert.


Danke, das hat mir wirklich geholfen, auf den richtigen Weg zu kommen. Ich suchte nach einer Lösung, bei der das eval "$(direnv export bash)"Elisp nicht hinzugefügt werden muss, aber dies war äußerst hilfreich und brachte mich auf den richtigen Weg.
— Waymondo

5

Running eval "$(direnv hook $0)"definiert eine Funktion, die sich einhakt $PROMPT_COMMANDund die beim Ausführen von bash niemals aufgerufen wird, bash -icda keine Eingabeaufforderung vorhanden ist. Sie können die Zeile ändern:

eval "$(direnv hook $0)"

zu:

eval "$(direnv hook $0)" && _direnv_hook

die Hook-Funktion explizit aufrufen.

Bearbeiten: Gerade realisiert, gab rekado eine sehr ähnliche Antwort.


Dies ist die prägnanteste Antwort, die darauf hinweist, dass ich nicht weiß, wie direnv funktioniert $PROMPT_COMMAND.
— Waymondo

4

Vielen Dank an Rekado und Erik für den Hinweis, wie der Direnv-Hook mit funktioniert $PROMPT_COMMAND. Da shell-commandkeine Eingabeaufforderung verwendet wird, wurde diese nicht ausgeführt.

Während Eriks Antwort in meinem Beispiel funktioniert, in dem ein Shell-Befehl M-!mit default-directoryset aufgerufen wird, funktioniert sie im folgenden Beispiel nicht:

(let ((default-directory "~/code/"))
  (shell-command "cd ~/code/foo && echo $PATH"))

Nach einigem googeln habe ich einen Weg gefunden, einen preexec-Hook in bash zu erstellen , um etwas auszuführen, bevor ein Befehl ausgeführt wird. Ich nahm diese Idee und modifizierte sie, um meinem Bedürfnis nach Direnv gerecht zu werden. So sieht der relevante Teil meines ~ / .bashrc jetzt aus:

eval "$(direnv hook $0)"
direnv_preexec () {
  [ -n "$COMP_LINE" ] && return # don't execute on completion
  [ "$BASH_COMMAND" \< "$PROMPT_COMMAND" ] && return # don't double execute on prompt hook
  eval "$(direnv export bash)"
}
trap "direnv_preexec && $BASH_COMMAND" DEBUG

0

Heutzutage möchten Sie wahrscheinlich https://github.com/wbolster/emacs-direnv verwenden

Es funktioniert ähnlich wie der Hook, den direnv in Ihrer Shell installiert. Die Emacs-Umgebung wird auf Anfrage (oder automatisch beim Wechseln der Puffer) aktualisiert, um den direkten Umgebungen für das aktuelle Verzeichnis zu entsprechen.

Durch Ändern exec-pathund process-environmentEmacs wird sich so verhalten, wie es Ihre Shell tun würde: Führen Sie Programme von den richtigen Pfaden und mit der richtigen Umgebung aus.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.