Skip to content
🐚 Helper for Emacs shell command APIs
Emacs Lisp
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Helper for Emacs shell command APIs, making implict argument as explicit keyword arguments.

This package is a library and does not provide any command.

It is inspired by the eval-after-load / with-eval-after-load functions.

For more context, read the accompanying blog post.


Not yet on Melpa.

For now, the recommended way to install is with use-package, quelpa and quelpa-use-package.

(use-package with-shell-interpreter
  :quelpa (with-shell-interpreter :fetcher github :repo "p3r7/with-shell-interpreter"))


We recommand using the macro with-shell-interpreter. It's a more convenient version of with-shell-interpreter-eval that prevents having to quote :form and wrap it in a progn.

keyword argument implicit var being let-bound mandatory? description
:form ✔️ The s-expressions to eval.
:path default-directory The path from which to eval.
:interpreter explicit-shell-file-name / shell-file-name Name or absolute path of shell interpreter executable.
:interpreter-args explicit-INTEPRETER-args Login args to call interpreter with for login.
:command-switch shell-command-switch Command switch arg for asking interpreter to run a shell command.
:w32-arg-quote w32-quote-process-args Character to use for quoting shell arguments (only on the Windows build of Emacs)

:form is expected to contain calls to functions relying on the Emacs shell APIs (e.g. shell, shell-command, async-shell-command and shell-command-to-string).

Setting :path to a remote location (with TRAMP format, i.e. /<method>:<user>@<host>:<localname>) allows running form with interpreter of remote server.

:interpreter-args is only usefull for interactive shells (from package shell-mode).

:command-switch is only usefull for single shell commands (from package simple).

If left empty, here are the default values being used:

keyword argument fallback value (local path) fallback value (remote path)
:path current default-directory current default-directory
:interpreter shell-file-name with-shell-interpreter-default-remoter
:interpreter-args explicit-INTEPRETER-args if set with-shell-interpreter-default-remote-args
:command-switch shell-command-switch with-shell-interpreter-default-remote-command-swith
:w32-arg-quote w32-quote-process-args w32-quote-process-args


Getting the temperature from a Raspberry Pi:

   :path "/ssh:pi@raspberry:/~"
   :interpreter "bash"
   (shell-command-to-string "vcgencmd measure_temp"))

Under Microsoft Windows, launching an interactive shell with the git-bash interpreter:

   :path "~"                            ; ensure local path
   :interpreter "C:/Program Files/Git/bin/bash.exe"
   (let (current-prefix-arg '(4))       ; don't prompt user for interpreter

For more practical examples, have a look at packages in prf-shell.


The package defines 3 variables for configuring the default interpreter for remote connections:

  • with-shell-interpreter-default-remote: takes precedence over shell-file-name. Default value is "/bin/bash".
  • with-shell-interpreter-default-remote-args: takes precedence over explicit-INTEPRETER-args. Default value is '("-c" "export EMACS=; export TERM=dumb; stty echo; bash").
  • with-shell-interpreter-default-remote-command-swith: takes precedence over shell-command-switch. Default value is -c.

We want this behavior as the user might have redefined the value of shell-file-name with something exotic (e.g. zsh) and we would want a safer default for remote servers.

Furthermore, under Microsoft Windows, shell-file-name defaults to cmdproxy.exe which is OK for local shells but sucks for remote ones...

These values can be overriden with keyword arguments :interpreter, :interpreter-args and :command-switch respectively.

Additionally, you might want to change the value of tramp-default-user if you usually connect to remote host with a user different than your local one.


This code uses form feeds (^L character) as separators.

Package form-feed makes them appear as intended.

Package lisp-extra-font-lock is also recommanded to distinguish between local and global vars in let expressions.

You can’t perform that action at this time.