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.