Kabuk profilini ve geçerli dizin kancalarını kullanarak kabuk komutunu çalıştırma (ör. Direnv)


9

Ben almaya çalışıyorum shell-commandve async-shell-commandbenim de programların bir çift ile sorunsuzca entegre etmek .bashrcözellikle dosyaya direnv bu örnekte.

Özelleştirirsem shell-command-switchprofilimi normal bir etkileşimli giriş kabuğu gibi yüklemek için kabuk süreçleri alabileceğimi fark ettim:

(setq shell-command-switch (salt kopya "-ic"))
(setq açık-bash-args '("-ic" "dışa aktarma EMACS =; stty echo; bash"))

Ben de exec-path-from-shell kullanıyorum .

Diyelim ki bir ~/.bashrcdosya var:

...

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

İçinde ~/code/foobir .envrcdosya var:

dışa aktarma PATH = $ PWD / bin: $ PATH
echo "bar"

Eğer set M-x shellile çalıştırırsam , bir bash kabuğu profilimi doğru şekilde yükler ve bunu yoluma eklemek için direnv kancasını çalıştırır:default-directory~/code/foo

direnv: yükleme .envrc
bar
direnv: ihracat ~ PATH
~ / kod / foo $ echo $ PATH
/ Kullanıcılar / kullanıcı adı / kod / foo / bin: / usr / local / bin: ... # $ PATH geri kalanı

Ancak default-directoryhala ~/code/foove ben çalışırsam , M-! echo $PATHdoğru benim .bashrc yükler ama geçerli dizinin direnv kanca yürütmez:

foo
/ usr / local / bin: ... #. /bin olmadan $ PATH'ın geri kalanı

Eğer koşarsam aynı sonucu alırım M-! cd ~/code/foo && echo $PATH.

Bir interaktif kabuk tamponundan gönderiliyormuş gibi davranmasını sağlamak shell-commandveya tavsiye etmek veya takmak için bir yol var mı start-process?


Direnv kancan nasıl görünüyor? Eğer ~ / .bashrc içinde tanımlanmışsa ve (setq shell-command-switch "-ic")değerlendirmişseniz, ~ / .bashrc içindeki diğer tüm komutlarla birlikte değerlendirilmelidir.
rekado

~ / .Bashrc dosyamdaki satır eval "$(direnv hook $0)". Bu yürütülüyor, ancak bir .envrcdosya ile belirli bir dizinde olduğunuzda ateşlenmesi gereken mekanizma değil.
waymondo

.envrcDosyadaki hiçbir şey değerlendirilmiyor mu? Yoksa sadece dışa aktarılmayan ortam değişkenleri mi? Bunu yeniden oluşturmayı deneyebilmem için lütfen tam bir örnek verebilir misiniz?
rekado

@rekado - Bir göz attığınız için teşekkürler. Sorumu çoğaltmak için daha açık olacak şekilde güncelledim.
waymondo

Yanıtlar:


5

Bu Emacs ile değil, bash ile ilgili bir sorun gibi görünüyor. shell-commandsadece call-processkabuk üzerinde çalışır ve argümanlar iletir. Bunu düzenli bir kabukta denedim:

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

~/.bashrckaynaklanır, ancak direnv kancası çalıştırılmaz. Ne zaman direnv hook bashyürütüldüğünde, bir fonksiyon _direnv_hookçıkışı ve başına ilave olduğunu PROMPT_COMMAND.

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

Bunun PROMPT_COMMANDişe yaramayacağından şüpheleniyorum . Bu bir sorun değil, çünkü _direnv_hookgerçekten basit. Biz sadece eval $(direnv export bash)shell komutunun başına geçebiliriz ve çalışır:

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

Bu, ileti arabelleğinin artırılmış yolunu yazdırır. Alternatif olarak M-!:

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

Bunun (setq shell-command-switch "-ic")çalışması için gerek yok .


Teşekkürler, bu gerçekten doğru yola girmeme yardımcı oldu. Ben eval "$(direnv export bash)"elisp eklemeyi içermeyen bir çözüm arıyordum , ama bu son derece yardımcı oldu ve beni doğru yola koydu.
waymondo

5

Çalıştırma eval "$(direnv hook $0)", $PROMPT_COMMANDbash çalıştırıldığında bash -ichiçbir zaman istem olmadığı için çağrılmayan, içine giren bir işlevi tanımlar . Çizgiyi değiştirebilirsiniz:

eval "$(direnv hook $0)"

için:

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

hook işlevini açıkça çağırmak için.

Edit: Sadece fark rekado çok benzer bir cevap verdi.


Bu, direnv'in nasıl çalıştığı konusuna aşina olmadığımı gösteren en kısa cevap $PROMPT_COMMAND.
waymondo

4

Direnv kancasının nasıl çalıştığını gösterdikleri için Rekado ve Erik'e teşekkürler $PROMPT_COMMAND. Yana shell-commandbir istem kullanmaz, bu idam elde değildi.

Erik'in yanıtı M-!, default-directoryset ile birlikte bir kabuk komutu çağırma örneğimde çalışırken, aşağıdaki örnekte işe yaramaz:

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

Bazı googling sonra, bir komut yürütülmeden önce bir şey yürütmek için bash içinde bir preexec kanca oluşturmak için bir yol buldum . Bu fikri aldım ve direnv ihtiyacımı karşılayacak şekilde değiştirdim. İşte ~ / .bashrc'nin ilgili kısmı şu şekilde görünüyor:

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

bugünlerde muhtemelen https://github.com/wbolster/emacs-direnv

bu, kabuğunuza monte edilen kancaya benzer şekilde çalışır. emacs ortamı istek üzerine (veya arabellekleri değiştirirken otomatik olarak) hangi dizinv durumlarının geçerli dizin için doğru ortam olduğu ile eşleşecek şekilde güncellenir.

değiştirerek exec-pathve process-environmentemacs kabuk gibi davranacaktır: programları doğru yollardan ve doğru ortam ile yürütmek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.