Jak uruchomić polecenie powłoki za pomocą profilu powłoki i zaczepów bieżącego katalogu (np. Direnv)

9

Próbuję dostać shell-commandi async-shell-commanddo integracji z kilku programów w moim .bashrcpliku, specjalnie direnv w tym przykładzie.

Przekonałem się, że jeśli dostosuję shell-command-switch, mogę uzyskać procesy powłoki ładujące mój profil, tak jakby to była zwykła interaktywna powłoka logowania:

(setq shell-command-switch (purecopy „-ic”))
(setq express-bash-args '(„-ic” „export EMACS =; stty echo; bash”))

Używam również exec-path-from-shell .

Powiedz, że mam ~/.bashrcplik z:

...

eval „$ (hak direnv 0 $)”
echo „foo”

W środku ~/code/foomam .envrcplik z:

eksport PATH = $ PWD / bin: $ PATH
„pasek” echa

Jeśli uruchomię M-x shellz default-directoryustawioną na ~/code/foo, powłoka bash poprawnie załaduje mój profil i uruchomi direnv hook, aby dodać go do mojej ścieżki:

direnv: ładowanie .envrc
bar
direnv: eksport ~ ŚCIEŻKA
~ / code / foo $ echo $ PATH
/ Users / nazwa użytkownika / kod / foo / bin: / usr / local / bin: ... # reszta $ PATH

Jednak jeśli default-directorynadal jest ~/code/foouruchomiony M-! echo $PATH, to poprawnie ładuje mój plik .bashrc, ale nie wykonuje haka direnv bieżącego katalogu:

bla
/ usr / local / bin: ... # reszta $ PATH bez ./bin

Otrzymuję ten sam wynik, jeśli biegnę M-! cd ~/code/foo && echo $PATH.

Czy istnieje sposób, w jaki mogę doradzić, podpiąć się shell-commandlub start-processsprawić, by zachowywał się tak, jakby był wysyłany z interaktywnego bufora powłoki?

waymondo
źródło
Jak wygląda Twój haczyk Direnv? Jeśli jest on zdefiniowany w ~ / .bashrc, a użytkownik ewaluował (setq shell-command-switch "-ic"), powinien zostać oceniony wraz z każdym innym poleceniem w ~ / .bashrc.
rekado
Linia w moim ~ / .bashrc to eval "$(direnv hook $0)". Jest to wykonywane, ale nie działa mechanizm, który powinien zostać uruchomiony, gdy jesteś w określonym katalogu z .envrcplikiem.
waymondo
Czy nic w .envrcpliku nie jest oceniane? Czy to tylko zmienne środowiskowe, które nie są eksportowane? Czy możesz podać pełny przykład, abym mógł spróbować to odtworzyć?
rekado
@rekado - Dziękujemy za przyjrzenie się temu. Zaktualizowałem moje pytanie, aby było bardziej jednoznaczne w zakresie powielania.
waymondo

Odpowiedzi:

5

To nie wydaje się być problemem w Emacsie, ale w bashu. shell-commandpo prostu wykonuje call-processsię na powłoce i przekazuje argumenty. Próbowałem tego na zwykłej powłoce:

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

~/.bashrcjest pozyskiwany, ale hak direnv nie jest uruchamiany. Po direnv hook bashuruchomieniu funkcja _direnv_hookjest wyprowadzana i dodawana do PROMPT_COMMAND.

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

Podejrzewam, że PROMPT_COMMANDpo prostu nie zadziała. Nie stanowi to jednak problemu, ponieważ _direnv_hookjest naprawdę prosty. Możemy po prostu poprzedzić eval $(direnv export bash)polecenie powłoki i zadziała:

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

Spowoduje to wydrukowanie rozszerzonej ścieżki do bufora komunikatów. Alternatywnie wykonaj za pomocą M-!:

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

Nie musisz (setq shell-command-switch "-ic")tego robić .

rekado
źródło
Dzięki, to naprawdę pomogło mi postawić właściwą drogę. Szukałem rozwiązania, które nie wymaga dodawania eval "$(direnv export bash)"elisp, ale było to niezwykle pomocne i skierowało mnie na właściwą ścieżkę.
waymondo
5

Uruchomienie eval "$(direnv hook $0)"definiuje funkcję, która się zaczepia $PROMPT_COMMAND, która nigdy nie jest wywoływana podczas uruchamiania bash, bash -icponieważ nie pojawia się monit. Możesz zmienić linię:

eval "$(direnv hook $0)"

do:

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

jawnie wywołać funkcję hook.

Edycja: Właśnie zrealizowane rekado dało bardzo podobną odpowiedź.

Erik Hetzner
źródło
To jest najbardziej zwięzła odpowiedź wskazująca na moją nieznajomość tego, jak działa direnv $PROMPT_COMMAND.
waymondo
4

Podziękowania dla Rekado i Erika za wskazanie, jak działa hak direnv za pomocą $PROMPT_COMMAND. Ponieważ shell-commandnie korzysta z monitu, nie można go było wykonać.

Podczas gdy odpowiedź Erika działa w moim przykładzie wywołania polecenia powłoki M-!z default-directoryustawieniem, nie zadziała w następującym przykładzie:

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

Po pewnym googlingu znalazłem sposób, aby utworzyć hak prexec w bash, aby wykonać coś przed wykonaniem polecenia. Przyjąłem ten pomysł i zmodyfikowałem go, aby pasował do mojej potrzeby direnv. Oto jak teraz wygląda odpowiednia część mojego ~ / .bashrc:

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
waymondo
źródło
0

w dzisiejszych czasach prawdopodobnie będziesz chciał skorzystać z https://github.com/wbolster/emacs-direnv

działa podobnie do haka, który direnv instaluje w twojej powłoce. środowisko emacs jest aktualizowane na żądanie (lub automatycznie podczas przełączania buforów) w celu dopasowania do stanów direnv, które są poprawnym środowiskiem dla bieżącego katalogu.

modyfikując exec-pathi process-environmentemacs będzie zachowywał się tak, jak zrobiłaby to twoja powłoka: uruchamiaj programy z właściwych ścieżek i we właściwym środowisku.

wouter bolsterlee
źródło