Presenting Eshell
Outline
- Introduction:
- Typical Shell Features
- Typical Lisp REPL features:
- Advanced Features
- Modifications/Extensions:
Note: We must make this into a workshop … type this, then this.
Introduction
John Wiegley created EShell in 1998:
…as a way to provide a UNIX-like environment on a Windows NT machine.
Part of Emacs since v21.
Personally?
- Started with
ksh
- Used a lot of shells…
- Tried
eshell
soon after its birth - Shelved it since it wasn’t shell-enough
- Rediscovered years later
- Finally got it…
What’s all this then?
- What eshell really is
- How to use
Hacking
FYI: Deep content coming up… Watch this as a recording later (I’ll upload this org-mode file)
Shell… The Good
- Can be immensely powerful… at times
- Pipes and redirection are a staple
- Utilizing small, focused text-oriented executables
- Complex command re-invocation (aka. shell history)
Shell… The Bad
- Commands? Like key sequences, only longer
- Needing completion to run commands?
- Loops? Not terrible
Shell… The WTF?
- Best part: extensibility!
But what an awful language:
if [ $(echo "$IN" | cut -c 1-3 ) == 'abc' ]; then # ... fi
- May be Turing complete, but so what.
But, but, but… we know the shell!
iPython
Python REPL with shell-like features.
- Understands a current directory
- Has some shell-like commands,
cat
- Doesn’t easily execute programs:
system
- Executes Python scripts:
run
EShell as a Shell
- Most “interactive language” interfaces choose:
- Language-specific REPL
- Shell-focused program worker
- Eshell marries both pretty well:
- Concept of a current directory
popd
,pushd
, anddirs
- Globbing Expressions
- Aliases:
alias ll 'ls -l'
- Tempted to think
eshell
is likeshell
EShell as a REPL
- Lisp expressions just work with parens
- Parens kinda optional (more later)
- Eshell distinguishes numbers from strings
- Quotes often optional
- Do you care about spaces?
- Double and single quotes are interchangeable
Function or Executable?
What about the executable find
vs.
Emacs’ find
function?
Precedence Order:
- Eshell aliases
- Emacs functions that being with
eshell/
prefix - Normal Emacs functions
(don’t need to be
interactive
) - Shell executables
Of course, this is customizable:
eshell-prefer-lisp-functions
prefer Lisp functions to external commandseshell-prefer-lisp-variables
prefer Lisp variables to environmentals
Functions should assume &rest
for arguments:
(defun eshell/do-work (&rest args) "Do some work in an optional directory." (let ((some-dir (if args (pop args) default-directory))) (message "Work in %s" some-dir)))
Eshell’s Parser Expressions
eshell
… can you say braces?- no parens
- assumes strings and globs $vars
{ ... }
- grouping expressions
${ ... }
- evaluation for strings
elisp
… all parens all the time( ... )
- is fully Lisp-centric
$( ... )
- evaluation for strings and string substitution
Note: You can mix your expression parsing
Shell-like Loops
- Syntactic sugar around
loop
. - Code following
in
is a generate list - Use trailing
{ ... }
for side-effects
Globbin’ Filters
- The
*
glob-thing has filters - Great if you can remember the syntax:
.
for files/
for directoriesr
if readablew
if writableL
filtering based on file sizem
filtering on modification time
- The filters can be stacked, e.g.
.L
Modifiers
Got a list of files?
Syntactic sugar to convert strings and lists.
Eshell filters and modifiers remind me of regular expressions
Don’t know the eshell-way? Just drizzle Lisp.
EShell Hack Points
While offering similar shell experience, Eshell is really hackable!
Extending Predicates
The User predicate (U)
could have been written:
(defun file-owned-current-uid-p (file) (if (file-exists-p file) (= (nth 2 (file-attributes file)) (user-uid))))
Then add it:
(add-hook 'eshell-pred-load-hook (lambda () (add-to-list 'eshell-predicate-alist '(?U . 'file-owned-current-uid-p))))
I have a directory stuffed with files.
Most of my files have #+tags
entries.
I can filter based on these tag entries.
I have to parse text following predicate key.
Remote Connections
To have eshell work on a remote server:
(let ((default-directory "/ssh:your-host.com:public/")) (eshell))
My personal project:
- Connect to my hypervisor controller
- Download and store a list of virtual machines
- Use
ido-completing-read
to select a host / ip - Generate a Tramp URL for
default-directory
Replacing Pipes
Pipes are Flexible, but…
- Shell’s text processing is limited
- Using arsenal of tiny, cryptic programs
- Re-run many times since debug pipe steps
Instead, redirect output to Emacs buffer:
$ some-command > #<buffer buf-name>
Reference buffers as #<buf-name>
with:
(setq eshell-buffer-shorthand t)
Emacs is pretty good at text processing
keep-lines
/flush-lines
instead ofgrep
replace-string
, et. al instead ofsed
Been thinking of something like:
$ openstack server list > pipe-edit saved-results.txt
Eshell Summary
- Advantages:
- Similar shell experience between operating systems
- Much more extendable, hackable and funner
- Disadvantages:
- Pipes go through Emacs buffers… not efficient
Programs that need special displays:
(add-to-list 'eshell-visual-commands "top")