I have 10 minutes to talk you into
giving Eshell a second chance.
Have the right perspective and expectation,
and I think you’ll really enjoy it.
Just remember eshell is a shell,
not a terminal emulator.
I use both Eshell and vterm.
I’m going to talk and type fast,
as I have 10 reasons for you to try Eshell again.
1. It’s an Emacs REPL.
I mean, check this out.
Let’s start up Eshell here.
Let’s just type a Lisp expression.
It works.
As a shell, the parens are kinda optional.
2. It’s also a shell.
While eshell may look like a shell, like Bash
you should view it as a REPL
with parenthesis-less s-expressions.
This makes sense, because a shell command with options,
like this ls command,
looks like an s-expression.
3. You can mix these two modes.
Shells can call subshells
which return their output like a function call,
like this Bash command.
In this Eshell example,
I use the output of a text file
as command line arguments to ripgrep.
Notice how I use braces
to state that it is a call to an eshell expression.
We can mix Lisp-expressions and Shell-expressions.
Allow me a contrived example.
Notice I use good ol' setq to create a variable.
Yes, those are global Emacs variables available everywhere.
In Eshell, the wildcard actually creates a list.
This variable assignment doesn’t work as you might expect,
as setq in Eshell is still setq,
and it assigns variables in pairs.
To make a list in Eshell, we use listify:
Without parens, Eshell is in “shell mode”,
which means that words are strings,
and variables need to be prefixed with dollar signs.
A command can have both Eshell and Lisp expressions.
As you can see here,
I have a call to ripgrep,
but part of it is an s-expression.
Remember the differences:
With parens, eshell treats it as Lisp,
like the last line in my example.
With braces, eshell follows these shell-like rules:
First, if it looks like a number, it's a number.
Otherwise, eshell converts it to a string
(quotes, like a shell, groups words).
What about this mix between functions and executables
for the first word?
Functions that begin with eshell are called first.
Next in priority are executables on your $PATH,
then matching Lisp functions.
You can actually switch this order
with the `eshell-prefer-lisp-functions` variable.
4. Emacs is actually better than shell.
If the following works, why would you call
expr or bc or dc, or any of those other calculators?
You can just call a Lisp expression.
Why call less or more when you could call view-file?
Here, I’ve aliased less to view-file.
Load it up, and it shows up in an Emacs mode.
Just like with less, if you hit q,
you go back to your Eshell terminal.
I do have an improvement, though.
The problem with view-file is
it takes a single file as an argument.
In a shell, we might want to view more than one.
So let’s make a solution to that.
This function will call the first function
with the first argument,
and the second function with each of the rest.
This allows me to make a version of less
that calls view-file on the first [argument] given,
but open in another window for each additional file.
5. Better regular expressions.
Can’t remember regular expressions when calling
grep or some other search function? Use the rx macro.
Here I call ripgrep again, but this time,
I’m using a Lisp expression calling the rx macro
to look for UUIDs in the files in my current directory.
But I have another improvement for this.
While the rx macro is freaking cool for Emacs Lisp,
it doesn’t always translate to regular expressions
accepted by most commands.
The (I have no idea how to pronounce this) pcre2el project
can convert from a Lisp regular expression
to Perl-compatible regular expressions (PCRE)
acceptable by most search commands.
I’ve created a new macro here, prx,
that translates the output of the rx macro.
This allows me to type something much more readable,
and probably easier to remember.
Certainly easier than this freaking regular expression.
I’ve got an even better improvement.
The rx macro with regular expression snippets
can be assigned to key words
that I can then take advantage of.
Now our command would be much simpler to type.
6. Loops are better with predicates.
Let’s say you want to remove the execute bit
from files that have it.
In a shell like bash, you need both a for loop and an if,
as you can see in this example.
With eshell, use a predicate to combine into a simple loop.
The paren x after a file glob
filters for only files marked as executable.
Now here is another improvement.
Since we often type loops to execute on one command,
what about creating a function
that can do this all in one go?
This do function splits the arguments on that double colon,
where the left side is a single statement to run,
and the right side is a list of files.
I have to append and flatten it
in order for it to work.
It loops through each file,
creating an eshell command with the file appended.
With this, I can remove the execute bit
on all CSV files that have it.
I see that my example wasn’t too good, as most commands
like chmod accept multiple files, but you get the idea.
In my final, larger form on my website,
I don’t assume the command expression accepts
a file as a final argument,
as I can also replace underscores with the filename.
7. Output of last command.
Most shells have a special variable
like $? for the exit code of the last command.
While reading through the source code,
I noticed that the $$ refers to
the output of the last command.
This seems pretty cool.
However, Eshell returns true or nil
when running external commands,
so accessing the output from a call to ls
doesn’t work as expected.
But this is Emacs.
We can fix that.
After running any command, eshell sets these four variables.
I can hook a function call after every Eshell command.
Using buffer-substring,
I store the output into a global variable,
and extend Eshell’s special variables list.
In my Emacs configuration,
I turned this variable into a ring,
so while $$ works,
so does array sub-scripting on that variable.
This allows me to run a command
and use the output from that command more than once.
The code for this is a bit longer,
so you’ll need to see my Emacs configuration for details.
8. Redirection back to Emacs.
Output of any command
can go to kill-ring (or the clipboard).
Think of the implications.
You don’t have to go into text selection mode.
Just grab the output.
In fact, with our $$ improvement,
we can always copy the output from the last command
to the clipboard.
Better yet, let’s write the output
to our engineering notebook.
Here’s my idea.
First, create a capture template that takes a string,
or if called interactively, the region,
and that does an immediate-finish after inserting
that string to the default notes file.
Next, create a wrapper function
to call org-capture-string to run that template.
Finally, we add our new function to eshell-virtual-targets.
Let’s see this in action.
I have a CSV file of user information.
I can use grep and cut to extract some of that
and write it out to this month’s engineering notebook.
9. Using Emacs buffers.
Why leave the results of eshell commands
in the *eshell* buffer?
Send the output into a buffer where you can use it.
Here’s a call to ripgrep
that searches for lines with email addresses
using a complicated regular expression
that I added to my prx macro.
When I switch to this almost-grep buffer,
I can turn on grep-mode.
Now I can jump around as if I just called grep directly.
Perhaps I’m proficient with my prx macro
to filter out entries,
but not good with shell commands
that I can use in pipes to extract just one…
the address column, for instance?
Let’s just extract it,
send it to a buffer called email-list,
and now I can use Emacs commands that I know and love
to edit the data directly.
We currently have an over-sight
that the Eshell’s built-in cat command
doesn’t pipe buffer contents as standard in.
So I created a bcat, a buffer cat, function to do this.
So this command works
to grab my email addresses I just extracted
and send them to another program.
If you’re interested, I have a more elaborate
and yet simpler workflow surrounding sending data
back and forth from Eshell to Emacs buffers.
10. Did I mention that you can cd to remote systems?
This command uses SSH to jump to my host, goblin,
start a root session, and jump to the etc directory.
Remember that Tramp can be finicky
if you start blinging your remote hosts with oh-my-zshell,
and funky prompts and things like that,
so your mileage may vary.
In summary: Use eshell if you want
a quick way to run commands and Emacs functions as a REPL,
or to run an OS program but process the output with Emacs.
Keep in mind that Eshell has two types of subshells,
and you can mix and match during a command call.
The rx macro is really cool.
Eshell loops are better with filters and predicates …
if you can remember them.
Take advantage of Emacs buffers
to really enhance your shell experience.
You’ve now seen that just like Emacs,
I’ve crafted Eshell to be my own shell creation,
tailored to my workflow.
So, steal my spells, cast your own magic,
but feel free to share your incantations back to me.
I’ve gone over my time allotment, so we’ll have to
continue this discussion on the intertubes.
Why yes, I have joined the birdless diaspora,
so toot me over there.
Thanks.