Come eseguire il comando shell utilizzando il profilo shell e gli hook di directory correnti (es. Direnv)


9

Sto cercando di ottenere shell-commande async-shell-commanddi integrarmi perfettamente con un paio di programmi nel mio .bashrcfile, in particolare direnv in questo esempio.

Ho scoperto che se avessi personalizzato shell-command-switch, avrei potuto ottenere processi shell per caricare il mio profilo come se fosse una normale shell di login interattivo:

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

Sto anche usando exec-path-from-shell .

Di 'che ho un ~/.bashrcfile con:

...

eval "$ (direnv hook $ 0)"
eco "pippo"

All'interno ~/code/fooho un .envrcfile con:

export PATH = $ PWD / bin: $ PATH
echo "bar"

Se corro M-x shellcon default-directoryset to ~/code/foo, una shell bash caricherà correttamente il mio profilo ed eseguirà il hook direnv per aggiungerlo al mio percorso:

direnv: caricamento .envrc
bar
direnv: export ~ PATH
~ / code / foo $ echo $ PERCORSO
/ Utenti / nome utente / codice / foo / bin: / usr / local / bin: ... # resto di $ PATH

Tuttavia, se default-directoryè ancora ~/code/fooe corro M-! echo $PATH, carica correttamente il mio .bashrc ma non esegue l'hook direnv della directory corrente:

foo
/ usr / local / bin: ... # resto di $ PATH senza ./bin

Ottengo lo stesso risultato se corro M-! cd ~/code/foo && echo $PATH.

C'è un modo in cui posso consigliare, agganciare shell-commando start-processfar sì che si comporti come se fosse inviato da un buffer di shell interattivo?


Che aspetto ha il tuo gancio direnv? Se è definito in ~ / .bashrc e lo hai valutato (setq shell-command-switch "-ic"), dovrebbe essere valutato insieme ad ogni altro comando in ~ / .bashrc.
rekado,

La linea nel mio ~ / .bashrc è eval "$(direnv hook $0)". Questo viene eseguito, ma non lo è il meccanismo che dovrebbe essere attivato quando ci si trova in una directory specifica con un .envrcfile.
Waymondo,

Non .envrcviene valutato nulla nel file? O sono solo le variabili d'ambiente che non vengono esportate? Potresti fornire un esempio completo in modo che io possa provare a riprodurlo?
rekado,

@rekado - Grazie per averlo dato un'occhiata. Ho aggiornato la mia domanda per essere più esplicito per la riproduzione.
Waymondo,

Risposte:


5

Questo non sembra essere un problema con Emacs ma con bash. shell-commandesegue solo call-processsulla shell e passa argomenti. Ho provato questo su una shell normale:

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

~/.bashrcviene fornito, ma l'hook direnv non viene eseguito. Quando direnv hook bashviene eseguita, una funzione _direnv_hookviene emessa e anteposta a PROMPT_COMMAND.

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

Sospetto che PROMPT_COMMANDsemplicemente non funzionerà. Questo non è un problema, però, perché _direnv_hookè davvero semplice. Possiamo semplicemente anteporre eval $(direnv export bash)al comando shell e funzionerà:

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

Ciò stamperà il percorso aumentato al buffer dei messaggi. In alternativa, eseguire con M-!:

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

Non è necessario (setq shell-command-switch "-ic")che funzioni.


Grazie, questo mi ha davvero aiutato a mettermi sulla buona strada. Stavo cercando una soluzione che non comportasse l'aggiunta di eval "$(direnv export bash)"in elisp, ma questo è stato estremamente utile e mi ha messo sulla strada giusta.
Waymondo,

5

L'esecuzione eval "$(direnv hook $0)"definisce una funzione che si aggancia $PROMPT_COMMAND, che non viene mai chiamata quando viene eseguito bash bash -icperché non c'è un prompt. Puoi cambiare la linea:

eval "$(direnv hook $0)"

per:

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

per chiamare esplicitamente la funzione hook.

Modifica: appena realizzato rekado ha dato una risposta molto simile.


Questa è la risposta più concisa sottolineando la mia non familiarità con il modo in cui lavora Direnv $PROMPT_COMMAND.
Waymondo,

4

Grazie a Rekado ed Erik per aver sottolineato come funziona l'hook direnv usando $PROMPT_COMMAND. Dal momento shell-commandche non utilizza un prompt, questo non veniva eseguito.

Mentre la risposta di Erik funziona nel mio esempio di chiamata di un comando shell M-!con default-directoryset, non funzionerebbe nell'esempio seguente:

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

Dopo aver cercato su Google , ho trovato il modo di creare un hook pre -ecec in bash per eseguire qualcosa prima dell'esecuzione di un comando. Ho preso questa idea e modificato per soddisfare il mio bisogno di Direnv. Ecco come appare adesso la parte rilevante del mio ~ / .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

oggi vorrai probabilmente usare https://github.com/wbolster/emacs-direnv

funziona in modo simile all'hook che direnv installa nella tua shell. l'ambiente emacs viene aggiornato su richiesta (o automaticamente quando si cambiano i buffer) in modo che corrisponda a ciò che direnv afferma sia l'ambiente corretto per la directory corrente.

modificando exec-pathed process-environmentemacs si comporterà come farebbe la tua shell: esegui programmi dai percorsi giusti e con l'ambiente giusto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.