วิธีทำให้คำสั่ง shell-shell รันโดยใช้เชลล์โปรไฟล์และ hooks ไดเรกทอรีปัจจุบัน (เช่น direnv)


9

ฉันพยายามที่จะรับshell-commandและasync-shell-commandบูรณาการอย่างราบรื่นกับสองสามโปรแกรมใน.bashrcไฟล์ของฉันโดยเฉพาะdirenvในตัวอย่างนี้

ฉันพบว่าถ้าฉันปรับแต่งshell-command-switchฉันจะได้รับกระบวนการเปลือกโหลดโปรไฟล์ของฉันราวกับว่ามันเป็นเปลือกเข้าสู่ระบบแบบโต้ตอบปกติ:

(setq shell-command-switch (purecopy "-ic"))
(setq ชัดแจ้ง -bash-args '("-ic" "ส่งออก EMACS =; stty echo; bash"))

นอกจากนี้ผมยังใช้exec เส้นทางจากเปลือก

ว่าฉันมี~/.bashrcไฟล์ด้วย:

...

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

ข้างใน~/code/fooฉันมี.envrcไฟล์ด้วย:

ส่งออก PATH = $ PWD / bin: $ PATH
echo "bar"

ถ้าฉันรันM-x shellด้วยdefault-directoryset to ~/code/foo, bash shell จะโหลดโปรไฟล์ของฉันอย่างถูกต้องและรัน direnv hook เพื่อเพิ่มเข้าไปในเส้นทางของฉัน:

direnv: กำลังโหลด. envrc
บาร์
direnv: ส่งออก ~ เส้นทาง
~ / code / foo $ echo $ PATH
/ ผู้ใช้ / ชื่อผู้ใช้ / รหัส / foo / bin: / usr / local / bin: ... # ส่วนที่เหลือของ $ PATH

อย่างไรก็ตามถ้าdefault-directoryยังคงอยู่~/code/fooและฉันรันM-! echo $PATHมันโหลด. bashrc ของฉันอย่างถูกต้อง แต่ไม่เรียกใช้ direnv hook ของไดเรกทอรีปัจจุบัน:

foo
/ usr / local / bin: ... # เหลือ $ PATH โดยไม่ต้อง. / bin

M-! cd ~/code/foo && echo $PATHฉันจะได้รับผลเหมือนกันถ้าผมทำงาน

มีวิธีที่ฉันสามารถแนะนำหรือขอเข้าไปshell-commandหรือstart-processทำให้มันทำงานราวกับว่ามันถูกส่งมาจากบัฟเฟอร์เปลือกโต้ตอบ?


ตะขอ direnv ของคุณมีหน้าตาเป็นอย่างไร? หากมีการกำหนดไว้ใน ~ / .bashrc และคุณประเมิน(setq shell-command-switch "-ic")แล้วควรประเมินพร้อมกับคำสั่งอื่น ๆ ใน ~ / .bashrc
rekado

บรรทัดในของฉัน ~ / .bashrc eval "$(direnv hook $0)"คือ นี่คือการดำเนินการ แต่กลไกที่ควรได้รับเมื่อคุณอยู่ในไดเรกทอรีเฉพาะกับ.envrcไฟล์ไม่ได้
waymondo

มีการ.envrcประเมินอะไรในไฟล์หรือไม่ หรือมันเป็นเพียงตัวแปรสภาพแวดล้อมที่ไม่ได้ถูกส่งออก? คุณช่วยยกตัวอย่างให้เต็มเพื่อที่ฉันจะได้ลองทำสิ่งนี้ซ้ำได้ไหม?
rekado

@rekado - ขอบคุณที่ดูมัน ฉันอัปเดตคำถามของฉันให้ชัดเจนยิ่งขึ้นสำหรับการทำซ้ำ
waymondo

คำตอบ:


5

ดูเหมือนจะไม่เป็นปัญหากับ Emacs แต่มีปัญหา shell-commandเพิ่งรันcall-processบนเชลล์และส่งผ่านข้อโต้แย้ง ฉันลองสิ่งนี้กับกระสุนธรรมดา:

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

~/.bashrcมีที่มา แต่เบ็ด direnv ไม่ทำงาน เมื่อdirenv hook bashถูกดำเนินการฟังก์ชั่นคือการส่งออกและใช้ได้กับ_direnv_hookPROMPT_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)"ใน elisp แต่มันมีประโยชน์มากและทำให้ฉันอยู่ในเส้นทางที่ถูกต้อง
waymondo

5

การทำงานจะeval "$(direnv hook $0)"กำหนดฟังก์ชั่นที่ขอเข้าไป$PROMPT_COMMANDซึ่งจะไม่ถูกเรียกเมื่อ bash ถูกเรียกใช้bash -icเนื่องจากไม่มีพรอมต์ คุณสามารถเปลี่ยนสาย:

eval "$(direnv hook $0)"

ถึง:

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

เพื่อเรียกใช้ฟังก์ชัน hook อย่างชัดเจน

แก้ไข: เพิ่งรู้ว่า rekado ให้คำตอบที่คล้ายกันมาก


นี่คือคำตอบที่รัดกุมมากที่สุดชี้ให้เห็นประสบการณ์ของฉันกับวิธีการทำงานของ direnv $PROMPT_COMMANDกับ
waymondo

4

ขอขอบคุณที่ Rekado และเอริคสำหรับการชี้ออกว่าเบ็ด direnv $PROMPT_COMMANDทำงานโดยใช้ เนื่องจากshell-commandไม่ได้ใช้พรอมต์สิ่งนี้จึงไม่ถูกเรียกใช้งาน

แม้ว่าคำตอบของ Erik จะทำงานในตัวอย่างของฉันในการเรียกคำสั่งเชลล์พร้อมM-!กับdefault-directoryชุด แต่มันจะไม่ทำงานในตัวอย่างต่อไปนี้:

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

หลังจาก googling ฉันพบวิธีสร้าง preexec hookใน 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 จะทำงานเหมือนที่เชลล์ทำ: รันโปรแกรมจากพา ธ ที่ถูกต้องและด้วยสภาพแวดล้อมที่เหมาะสม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.