Як зробити запуск команди оболонки за допомогою профілю оболонки та поточних гаків каталогів (наприклад, direnv)


9

Я намагаюся отримати shell-commandта async-shell-commandінтегруватись безперечно з парою програм у моєму .bashrcфайлі, зокрема direnv у цьому прикладі.

Я виявив, що якщо я налаштував shell-command-switch, я можу отримати процеси оболонки, щоб завантажити мій профіль, як якщо б це була звичайна інтерактивна оболонка входу:

(setq shell-command-switch (чистокопія "-ic"))
(setq явно-bash-args '("-ic" "експорт EMACS =; stty echo; bash"))

Я також використовую exec-path-from-shell .

Скажіть, у мене є ~/.bashrcфайл із:

...

eval "$ (гачок direnv $ 0)"
відлуння "foo"

Всередині у ~/code/fooмене є .envrcфайл із:

експорт PATH = $ PWD / бін: $ PATH
відлуння "бар"

Якщо я запускаю M-x shellз default-directoryвстановленим на ~/code/foo, оболонка bash правильно завантажить мій профіль і запустить гачок direnv, щоб додати це до мого шляху:

direnv: завантаження .envrc
бар
direnv: експорт ~ ПАТ
~ / code / foo $ echo $ PATH
/ Користувачі / ім'я користувача / код / ​​foo / bin: / usr / local / bin: ... # решта $ PATH

Однак якщо default-directoryвсе-таки є ~/code/fooі я запускаю M-! echo $PATH, він правильно завантажує мій .bashrc, але не виконує гачок direnv поточного каталогу:

foo
/ usr / local / bin: ... # решта $ PATH без ./bin

Я отримую такий же результат, якщо бігаю M-! cd ~/code/foo && echo $PATH.

Чи є спосіб, який я можу порадити чи підключити shell-commandабо start-processзмусити його вести себе так, ніби він надсилається з інтерактивного буфера оболонки?


Як виглядає ваш гачок direnv? Якщо він визначений у ~ / .bashrc, і ви eval'd, (setq shell-command-switch "-ic")його слід оцінювати разом з усіма іншими командами в ~ / .bashrc.
rekado

Рядок у моєму ~ / .bashrc є eval "$(direnv hook $0)". Це виконується, але механізм, який повинен бути звільнений, коли ви знаходитесь у певному каталозі з .envrcфайлом, відсутній.
waymondo

Чи нічого у .envrcфайлі не оцінено? Або це просто змінні середовища, які не експортуються? Чи можете ви надати повний приклад, щоб я спробував це відтворити?
rekado

@rekado - Дякую, що подивився на це. Я оновив своє запитання, щоб бути більш чітким для відтворення.
waymondo

Відповіді:


5

Мабуть, це не проблема з Emacs, а з bash. shell-commandпросто виконується call-processна оболонці і передає аргументи. Я спробував це на звичайній оболонці:

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

~/.bashrcджерело, але гачок direnv не запущений. Коли direnv hook bashце виконується, функція _direnv_hookвиводиться і передчувається PROMPT_COMMAND.

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

Я підозрюю, що PROMPT_COMMANDпросто не вийде. Але це не проблема, тому що _direnv_hookце дуже просто. Ми можемо просто додати eval $(direnv export bash)команду shell, і вона спрацює:

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

Це надрукує доповнений шлях до буфера повідомлень. Крім того, виконайте M-!:

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

Вам не потрібно (setq shell-command-switch "-ic")для цього працювати.


Дякую, це справді допомогло поставити мене на правильний шлях. Я шукав рішення, яке не передбачає додавання eval "$(direnv export bash)"в elisp, але це було надзвичайно корисно і поставило мене на правильний шлях.
waymondo

5

Запуск eval "$(direnv hook $0)"визначає функцію, що зачіпається $PROMPT_COMMAND, яка ніколи не викликається при запуску bash, bash -icтому що немає підказок. Ви можете змінити лінію:

eval "$(direnv hook $0)"

до:

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

явно викликати функцію гака.

Редагувати: Щойно зрозумів, що Рекадо дав дуже схожу відповідь.


Це найкоротша відповідь, яка вказує на мою незнайомість з тим, як працює direnv $PROMPT_COMMAND.
waymondo

4

Дякуємо Рекадо та Еріку за те, що вони вказали, як працює гачок direnv, використовуючи його $PROMPT_COMMAND. Оскільки shell-commandне використовує підказку, це не виконується.

Хоча відповідь Еріка працює на моєму прикладі виклику команди оболонки M-!з default-directoryнабором, вона не буде працювати в наступному прикладі:

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

Після деякого googling я знайшов спосіб створити гачок preexec в bash, щоб виконати щось перед виконанням команди. Я взяв цю ідею та змінив, щоб відповідати моїм потребам у direnv. Ось як виглядає відповідна частина мого ~ / .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

0

сьогодні ви, ймовірно, захочете скористатися https://github.com/wbolster/emacs-direnv

він працює аналогічно гачку, який direnv встановлює у вашій оболонці. середовище emacs оновлюється на запит (або автоматично при перемиканні буферів), щоб відповідати тому, що вказано direnv, є правильним середовищем для поточного каталогу.

змінюючи exec-pathі process-environmentemacs буде вести себе так, як це робила ваша оболонка: виконувати програми з правильних шляхів та з правильного середовища.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.