シェルプロファイルと現在のディレクトリフックを使用してシェルコマンドを実行する方法(例:direnv)


9

私はファイル内のいくつかのプログラム、特にこの例ではdirenvを取得shell-commandasync-shell-commandてシームレスに統合しようとしてい.bashrcます。

をカスタマイズした場合shell-command-switch、通常のインタラクティブログインシェルであるかのように、シェルプロセスでプロファイルをロードできることがわかりました。

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

私はexec-path-from-shellも使用しています

次のような~/.bashrcファイルがあるとします。

...

eval "$(direnv hook $ 0)"
「foo」をエコー

内部~/code/fooには次の.envrcファイルがあります:

エクスポートPATH = $ PWD / bin:$ PATH
「バー」をエコー

set to で実行するM-x shellと、bashシェルはプロファイルを正しくロードし、direnvフックを実行してパスに追加します。default-directory~/code/foo

direnv:.envrcの読み込み
バー
direnv:エクスポート〜PATH
〜/ code / foo $ echo $ PATH
/ Users / username / code / foo / bin:/ usr / local / bin:...#残りの$ PATH

ただし、default-directoryそれでもまだ~/code/foo実行している場合はM-! echo $PATH、.bashrcは正しく読み込まれますが、現在のディレクトリのdirenvフックは実行されません。

foo
/ usr / local / bin:...#./binなしの$ PATHの残り

を実行しても同じ結果が得られますM-! cd ~/code/foo && echo $PATH

対話型のシェルバッファーから送信されているshell-commandかのようにアドバイスしたり、フックしたりstart-process、動作させる方法はありますか?


direnvフックはどのように見えますか?〜/ .bashrcで定義されていて、(setq shell-command-switch "-ic")評価した場合は、〜/ .bashrcの他のすべてのコマンドとともに評価する必要があります。
rekado 2014年

〜/ .bashrcの行はeval "$(direnv hook $0)"です。これは実行されてい.envrcますが、ファイルのある特定のディレクトリにいるときに発生するはずのメカニズムは実行されません。
waymondo 2014年

.envrc評価されたファイルには何もありませんか?それとも、エクスポートされないのは環境変数だけですか?これを再現できるように、完全な例を提供していただけませんか?
rekado 2014年

@rekado-ご覧いただきありがとうございます。質問を更新して、より明確に再現できるようにしました。
waymondo 2014年

回答:


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)シェルコマンドの前に追加するだけで機能します。

(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)"in elisp を追加することを含まないソリューションを探していましたが、これは非常に役に立ち、正しい道に進みました。
waymondo 2014年

5

Running eval "$(direnv hook $0)"は、にフックする関数を定義します。これは、プロンプトがないため$PROMPT_COMMAND、bashの実行時に呼び出されるbash -icことはありません。次の行を変更できます。

eval "$(direnv hook $0)"

に:

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

フック関数を明示的に呼び出すため。

編集:rekadoが非常によく似た答えを出したことに気付いたところです。


これは、direnvがどのように機能するかについての不慣れを指摘する最も簡潔な回答$PROMPT_COMMANDです。
waymondo 2014年

4

を使用してdirenvフックがどのように機能するかを指摘してくれたRekadoとErikに感謝し$PROMPT_COMMANDます。以来shell-commandプロンプトを使用していない、これが実行されていませんでした。

エリックの答えはでシェルコマンドを呼び出すの私の例では動作しますがM-!付きdefault-directoryセット、それは次の例では機能しません。

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

いくつかのグーグルの後、私はコマンドが実行される前に何かを実行するためにbashでpreexecフックを作成する方法見つけました。私はこのアイデアを取り入れ、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はシェルと同じように動作します。つまり、適切なパスから適切な環境でプログラムを実行します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.