WEBVTT captioned by howard
NOTE Introduction
00:00:00.000 --> 00:00:04.999
I have 10 minutes to talk you into
00:00:05.000 --> 00:00:07.879
giving Eshell a second chance.
00:00:07.880 --> 00:00:10.119
Have the right perspective and expectation,
00:00:10.120 --> 00:00:12.919
and I think you’ll really enjoy it.
00:00:12.920 --> 00:00:15.679
Just remember eshell is a shell,
00:00:15.680 --> 00:00:17.839
not a terminal emulator.
00:00:17.840 --> 00:00:20.279
I use both Eshell and vterm.
00:00:20.280 --> 00:00:23.479
I’m going to talk and type fast,
00:00:23.480 --> 00:00:28.999
as I have 10 reasons for you to try Eshell again.
NOTE 1. It’s an Emacs REPL
00:00:29.000 --> 00:00:32.599
1. It’s an Emacs REPL.
00:00:32.600 --> 00:00:33.999
I mean, check this out.
00:00:34.000 --> 00:00:36.999
Let’s start up Eshell here.
00:00:37.000 --> 00:00:41.399
Let’s just type a Lisp expression.
00:00:41.400 --> 00:00:43.919
It works.
00:00:43.920 --> 00:00:48.599
As a shell, the parens are kinda optional.
NOTE 2. It’s also a shell
00:00:48.600 --> 00:00:52.519
2. It’s also a shell.
00:00:52.520 --> 00:00:56.479
While eshell may look like a shell, like Bash
00:00:56.480 --> 00:00:58.559
you should view it as a REPL
00:00:58.560 --> 00:01:02.399
with parenthesis-less s-expressions.
00:01:02.400 --> 00:01:05.559
This makes sense, because a shell command with options,
00:01:05.560 --> 00:01:07.999
like this ls command,
00:01:08.000 --> 00:01:10.119
looks like an s-expression.
NOTE 3. You can mix these two modes
00:01:10.120 --> 00:01:12.879
3. You can mix these two modes.
00:01:12.880 --> 00:01:14.959
Shells can call subshells
00:01:14.960 --> 00:01:17.919
which return their output like a function call,
00:01:17.920 --> 00:01:20.799
like this Bash command.
00:01:20.800 --> 00:01:22.759
In this Eshell example,
00:01:22.760 --> 00:01:24.639
I use the output of a text file
00:01:24.640 --> 00:01:27.959
as command line arguments to ripgrep.
00:01:27.960 --> 00:01:29.639
Notice how I use braces
00:01:29.640 --> 00:01:34.759
to state that it is a call to an eshell expression.
00:01:34.760 --> 00:01:40.039
We can mix Lisp-expressions and Shell-expressions.
00:01:40.040 --> 00:01:45.599
Allow me a contrived example.
00:01:45.600 --> 00:01:50.079
Notice I use good ol' setq to create a variable.
00:01:50.080 --> 00:01:54.919
Yes, those are global Emacs variables available everywhere.
00:01:54.920 --> 00:01:59.599
In Eshell, the wildcard actually creates a list.
00:01:59.600 --> 00:02:04.479
This variable assignment doesn’t work as you might expect,
00:02:04.480 --> 00:02:07.559
as setq in Eshell is still setq,
00:02:07.560 --> 00:02:10.319
and it assigns variables in pairs.
00:02:10.320 --> 00:02:17.119
To make a list in Eshell, we use listify:
00:02:17.120 --> 00:02:21.239
Without parens, Eshell is in “shell mode”,
00:02:21.240 --> 00:02:23.799
which means that words are strings,
00:02:23.800 --> 00:02:26.879
and variables need to be prefixed with dollar signs.
00:02:26.880 --> 00:02:32.399
A command can have both Eshell and Lisp expressions.
00:02:32.400 --> 00:02:34.559
As you can see here,
00:02:34.560 --> 00:02:37.119
I have a call to ripgrep,
00:02:37.120 --> 00:02:40.319
but part of it is an s-expression.
00:02:40.320 --> 00:02:42.239
Remember the differences:
00:02:42.240 --> 00:02:46.159
With parens, eshell treats it as Lisp,
00:02:46.160 --> 00:02:49.199
like the last line in my example.
00:02:49.200 --> 00:02:53.919
With braces, eshell follows these shell-like rules:
00:02:53.920 --> 00:02:57.159
First, if it looks like a number, it's a number.
00:02:57.160 --> 00:02:59.439
Otherwise, eshell converts it to a string
00:02:59.440 --> 00:03:03.679
(quotes, like a shell, groups words).
00:03:03.680 --> 00:03:07.519
What about this mix between functions and executables
00:03:07.520 --> 00:03:10.839
for the first word?
00:03:10.840 --> 00:03:15.439
Functions that begin with eshell are called first.
00:03:15.440 --> 00:03:19.079
Next in priority are executables on your $PATH,
00:03:19.080 --> 00:03:22.159
then matching Lisp functions.
00:03:22.160 --> 00:03:23.940
You can actually switch this order
00:03:23.941 --> 00:03:27.559
with the `eshell-prefer-lisp-functions` variable.
NOTE 4. Emacs is better than shell
00:03:27.560 --> 00:03:31.759
4. Emacs is actually better than shell.
00:03:31.760 --> 00:03:35.199
If the following works, why would you call
00:03:35.200 --> 00:03:40.039
expr or bc or dc, or any of those other calculators?
00:03:40.040 --> 00:03:43.639
You can just call a Lisp expression.
00:03:43.640 --> 00:03:47.999
Why call less or more when you could call view-file?
00:03:48.000 --> 00:03:52.839
Here, I’ve aliased less to view-file.
00:03:52.840 --> 00:03:57.559
Load it up, and it shows up in an Emacs mode.
00:03:57.560 --> 00:04:01.519
Just like with less, if you hit q,
00:04:01.520 --> 00:04:05.759
you go back to your Eshell terminal.
00:04:05.760 --> 00:04:08.439
I do have an improvement, though.
00:04:08.440 --> 00:04:10.479
The problem with view-file is
00:04:10.480 --> 00:04:13.399
it takes a single file as an argument.
00:04:13.400 --> 00:04:15.719
In a shell, we might want to view more than one.
00:04:15.720 --> 00:04:18.719
So let’s make a solution to that.
00:04:18.720 --> 00:04:20.999
This function will call the first function
00:04:21.000 --> 00:04:22.159
with the first argument,
00:04:22.160 --> 00:04:26.679
and the second function with each of the rest.
00:04:26.680 --> 00:04:29.559
This allows me to make a version of less
00:04:29.560 --> 00:04:33.159
that calls view-file on the first [argument] given,
00:04:33.160 --> 00:04:36.079
but open in another window for each additional file.
NOTE 5. Better regular expressions
00:04:36.080 --> 00:04:41.239
5. Better regular expressions.
00:04:41.240 --> 00:04:44.799
Can’t remember regular expressions when calling
00:04:44.800 --> 00:04:48.639
grep or some other search function? Use the rx macro.
00:04:48.640 --> 00:04:55.919
Here I call ripgrep again, but this time,
00:04:55.920 --> 00:05:00.679
I’m using a Lisp expression calling the rx macro
00:05:00.680 --> 00:05:04.719
to look for UUIDs in the files in my current directory.
00:05:04.720 --> 00:05:08.159
But I have another improvement for this.
00:05:08.160 --> 00:05:13.479
While the rx macro is freaking cool for Emacs Lisp,
00:05:13.480 --> 00:05:15.919
it doesn’t always translate to regular expressions
00:05:15.920 --> 00:05:20.079
accepted by most commands.
00:05:20.080 --> 00:05:25.199
The (I have no idea how to pronounce this) pcre2el project
00:05:25.200 --> 00:05:28.519
can convert from a Lisp regular expression
00:05:28.520 --> 00:05:31.359
to Perl-compatible regular expressions (PCRE)
00:05:31.360 --> 00:05:33.519
acceptable by most search commands.
00:05:33.520 --> 00:05:37.879
I’ve created a new macro here, prx,
00:05:37.880 --> 00:05:41.319
that translates the output of the rx macro.
00:05:41.320 --> 00:05:46.519
This allows me to type something much more readable,
00:05:46.520 --> 00:05:48.519
and probably easier to remember.
00:05:48.520 --> 00:05:54.679
Certainly easier than this freaking regular expression.
00:05:54.680 --> 00:05:59.439
I’ve got an even better improvement.
00:05:59.440 --> 00:06:03.559
The rx macro with regular expression snippets
00:06:03.560 --> 00:06:05.759
can be assigned to key words
00:06:05.760 --> 00:06:08.679
that I can then take advantage of.
00:06:08.680 --> 00:06:13.479
Now our command would be much simpler to type.
NOTE 6. Loops are better with predicates
00:06:13.480 --> 00:06:16.159
6. Loops are better with predicates.
00:06:16.160 --> 00:06:18.759
Let’s say you want to remove the execute bit
00:06:18.760 --> 00:06:20.479
from files that have it.
00:06:20.480 --> 00:06:24.399
In a shell like bash, you need both a for loop and an if,
00:06:24.400 --> 00:06:26.599
as you can see in this example.
00:06:26.600 --> 00:06:31.559
With eshell, use a predicate to combine into a simple loop.
00:06:31.560 --> 00:06:34.359
The paren x after a file glob
00:06:34.360 --> 00:06:36.879
filters for only files marked as executable.
00:06:36.880 --> 00:06:43.559
Now here is another improvement.
00:06:43.560 --> 00:06:47.959
Since we often type loops to execute on one command,
00:06:47.960 --> 00:06:49.519
what about creating a function
00:06:49.520 --> 00:06:50.999
that can do this all in one go?
00:06:51.000 --> 00:06:57.599
This do function splits the arguments on that double colon,
00:06:57.600 --> 00:07:00.079
where the left side is a single statement to run,
00:07:00.080 --> 00:07:02.599
and the right side is a list of files.
00:07:02.600 --> 00:07:05.839
I have to append and flatten it
00:07:05.840 --> 00:07:07.639
in order for it to work.
00:07:07.640 --> 00:07:09.399
It loops through each file,
00:07:09.400 --> 00:07:12.079
creating an eshell command with the file appended.
00:07:12.080 --> 00:07:15.759
With this, I can remove the execute bit
00:07:15.760 --> 00:07:20.759
on all CSV files that have it.
00:07:20.760 --> 00:07:24.319
I see that my example wasn’t too good, as most commands
00:07:24.320 --> 00:07:29.039
like chmod accept multiple files, but you get the idea.
00:07:29.040 --> 00:07:33.159
In my final, larger form on my website,
00:07:33.160 --> 00:07:35.279
I don’t assume the command expression accepts
00:07:35.280 --> 00:07:36.719
a file as a final argument,
00:07:36.720 --> 00:07:39.639
as I can also replace underscores with the filename.
NOTE 7. Output of last command
00:07:39.640 --> 00:07:45.399
7. Output of last command.
00:07:45.400 --> 00:07:48.799
Most shells have a special variable
00:07:48.800 --> 00:07:52.839
like $? for the exit code of the last command.
00:07:52.840 --> 00:07:55.919
While reading through the source code,
00:07:55.920 --> 00:07:58.799
I noticed that the $$ refers to
00:07:58.800 --> 00:08:00.599
the output of the last command.
00:08:00.600 --> 00:08:05.799
This seems pretty cool.
00:08:05.800 --> 00:08:10.759
However, Eshell returns true or nil
00:08:10.760 --> 00:08:12.719
when running external commands,
00:08:12.720 --> 00:08:15.879
so accessing the output from a call to ls
00:08:15.880 --> 00:08:19.479
doesn’t work as expected.
00:08:19.480 --> 00:08:21.119
But this is Emacs.
00:08:21.120 --> 00:08:23.159
We can fix that.
00:08:23.160 --> 00:08:28.119
After running any command, eshell sets these four variables.
00:08:28.120 --> 00:08:33.519
I can hook a function call after every Eshell command.
00:08:33.520 --> 00:08:36.759
Using buffer-substring,
00:08:36.760 --> 00:08:39.279
I store the output into a global variable,
00:08:39.280 --> 00:08:43.599
and extend Eshell’s special variables list.
00:08:43.600 --> 00:08:46.519
In my Emacs configuration,
00:08:46.520 --> 00:08:48.479
I turned this variable into a ring,
00:08:48.480 --> 00:08:51.439
so while $$ works,
00:08:51.440 --> 00:08:54.399
so does array sub-scripting on that variable.
00:08:54.400 --> 00:08:58.399
This allows me to run a command
00:08:58.400 --> 00:09:02.279
and use the output from that command more than once.
00:09:02.280 --> 00:09:05.279
The code for this is a bit longer,
00:09:05.280 --> 00:09:08.519
so you’ll need to see my Emacs configuration for details.
NOTE 8. Redirection back to Emacs
00:09:08.520 --> 00:09:13.439
8. Redirection back to Emacs.
00:09:13.440 --> 00:09:14.879
Output of any command
00:09:14.880 --> 00:09:18.519
can go to kill-ring (or the clipboard).
00:09:18.520 --> 00:09:21.079
Think of the implications.
00:09:21.080 --> 00:09:23.839
You don’t have to go into text selection mode.
00:09:23.840 --> 00:09:26.239
Just grab the output.
00:09:26.240 --> 00:09:30.279
In fact, with our $$ improvement,
00:09:30.280 --> 00:09:33.239
we can always copy the output from the last command
00:09:33.240 --> 00:09:34.079
to the clipboard.
00:09:34.080 --> 00:09:37.999
Better yet, let’s write the output
00:09:38.000 --> 00:09:39.399
to our engineering notebook.
00:09:39.400 --> 00:09:41.679
Here’s my idea.
00:09:41.680 --> 00:09:46.079
First, create a capture template that takes a string,
00:09:46.080 --> 00:09:48.199
or if called interactively, the region,
00:09:48.200 --> 00:09:51.879
and that does an immediate-finish after inserting
00:09:51.880 --> 00:09:53.879
that string to the default notes file.
00:09:53.880 --> 00:09:57.679
Next, create a wrapper function
00:09:57.680 --> 00:10:01.559
to call org-capture-string to run that template.
00:10:01.560 --> 00:10:07.639
Finally, we add our new function to eshell-virtual-targets.
00:10:07.640 --> 00:10:08.759
Let’s see this in action.
00:10:08.760 --> 00:10:15.707
I have a CSV file of user information.
00:10:15.708 --> 00:10:19.719
I can use grep and cut to extract some of that
00:10:19.720 --> 00:10:26.879
and write it out to this month’s engineering notebook.
NOTE 9. Using Emacs buffers
00:10:26.880 --> 00:10:35.279
9. Using Emacs buffers.
00:10:35.280 --> 00:10:39.159
Why leave the results of eshell commands
00:10:39.160 --> 00:10:40.279
in the *eshell* buffer?
00:10:40.280 --> 00:10:44.119
Send the output into a buffer where you can use it.
00:10:44.120 --> 00:10:47.999
Here’s a call to ripgrep
00:10:48.000 --> 00:10:50.759
that searches for lines with email addresses
00:10:50.760 --> 00:10:53.519
using a complicated regular expression
00:10:53.520 --> 00:10:56.079
that I added to my prx macro.
00:10:56.080 --> 00:11:01.079
When I switch to this almost-grep buffer,
00:11:01.080 --> 00:11:03.319
I can turn on grep-mode.
00:11:03.320 --> 00:11:09.039
Now I can jump around as if I just called grep directly.
00:11:09.040 --> 00:11:14.759
Perhaps I’m proficient with my prx macro
00:11:14.760 --> 00:11:16.639
to filter out entries,
00:11:16.640 --> 00:11:19.279
but not good with shell commands
00:11:19.280 --> 00:11:23.999
that I can use in pipes to extract just one…
00:11:24.000 --> 00:11:26.039
the address column, for instance?
00:11:26.040 --> 00:11:28.959
Let’s just extract it,
00:11:28.960 --> 00:11:33.279
send it to a buffer called email-list,
00:11:33.280 --> 00:11:38.479
and now I can use Emacs commands that I know and love
00:11:38.480 --> 00:11:39.799
to edit the data directly.
00:11:39.800 --> 00:11:55.799
We currently have an over-sight
00:11:55.800 --> 00:11:58.839
that the Eshell’s built-in cat command
00:11:58.840 --> 00:12:02.719
doesn’t pipe buffer contents as standard in.
00:12:02.720 --> 00:12:07.919
So I created a bcat, a buffer cat, function to do this.
00:12:07.920 --> 00:12:09.879
So this command works
00:12:09.880 --> 00:12:14.599
to grab my email addresses I just extracted
00:12:14.600 --> 00:12:16.319
and send them to another program.
00:12:16.320 --> 00:12:20.959
If you’re interested, I have a more elaborate
00:12:20.960 --> 00:12:25.759
and yet simpler workflow surrounding sending data
00:12:25.760 --> 00:12:28.399
back and forth from Eshell to Emacs buffers.
NOTE 10. cd to remote systems
00:12:28.400 --> 00:12:35.679
10. Did I mention that you can cd to remote systems?
00:12:35.680 --> 00:12:39.879
This command uses SSH to jump to my host, goblin,
00:12:39.880 --> 00:12:44.039
start a root session, and jump to the etc directory.
00:12:44.040 --> 00:12:47.719
Remember that Tramp can be finicky
00:12:47.720 --> 00:12:52.839
if you start blinging your remote hosts with oh-my-zshell,
00:12:52.840 --> 00:12:57.790
and funky prompts and things like that,
00:12:57.791 --> 00:12:59.359
so your mileage may vary.
NOTE Summary
00:12:59.360 --> 00:13:03.959
In summary: Use eshell if you want
00:13:03.960 --> 00:13:07.319
a quick way to run commands and Emacs functions as a REPL,
00:13:07.320 --> 00:13:11.479
or to run an OS program but process the output with Emacs.
00:13:11.480 --> 00:13:15.919
Keep in mind that Eshell has two types of subshells,
00:13:15.920 --> 00:13:19.599
and you can mix and match during a command call.
00:13:19.600 --> 00:13:22.639
The rx macro is really cool.
00:13:22.640 --> 00:13:26.599
Eshell loops are better with filters and predicates …
00:13:26.600 --> 00:13:28.239
if you can remember them.
00:13:28.240 --> 00:13:30.959
Take advantage of Emacs buffers
00:13:30.960 --> 00:13:32.879
to really enhance your shell experience.
00:13:32.880 --> 00:13:36.039
You’ve now seen that just like Emacs,
00:13:36.040 --> 00:13:39.519
I’ve crafted Eshell to be my own shell creation,
00:13:39.520 --> 00:13:41.039
tailored to my workflow.
00:13:41.040 --> 00:13:44.799
So, steal my spells, cast your own magic,
00:13:44.800 --> 00:13:48.759
but feel free to share your incantations back to me.
00:13:48.760 --> 00:13:51.359
I’ve gone over my time allotment, so we’ll have to
00:13:51.360 --> 00:13:53.679
continue this discussion on the intertubes.
00:13:53.680 --> 00:13:57.159
Why yes, I have joined the birdless diaspora,
00:13:57.160 --> 00:13:59.199
so toot me over there.
00:13:59.200 --> 00:14:01.920
Thanks.