In eshell, expanded my "set" to also set variables in environment

Also, converted my 'execute" command on a buffer file to use the shell
instead of eshell (as eshell's use of buffers is odd and I find it
disconcerting). Not sure what I would like here, though.
This commit is contained in:
Howard Abrams 2024-08-10 12:27:57 -07:00
parent 85a7613f69
commit 3042ffe460

View file

@ -97,11 +97,24 @@ In short, use parens to limit the files, for instance:
ls *.sh(*) # List shell scripts that are executable ls *.sh(*) # List shell scripts that are executable
ls *(/) # List directories (recursively) ls *(/) # List directories (recursively)
#+end_src #+end_src
And parens with a colon character transform the filename, useful with =for=: And parens with a colon character transform the filename, useful with =for=:
#+begin_src sh #+begin_src sh
for F in *.org(:r) { mv $F.org $F.el } for F in *.org(:r) { mv $F.org $F.el }
#+end_src #+end_src
Keep in mind that the predicates are somewhat finicky. For instance, the following doesnt work:
#+begin_src sh
mv $f $f(:r).txt
#+end_src
But you could call this:
#+begin_src sh
mv $f $f(:s/org$/txt/)
#+end_src
The =T= predicate filter allows me to limit file results that have internal =org-mode= tags. The =T= predicate filter allows me to limit file results that have internal =org-mode= tags.
#+begin_src sh #+begin_src sh
$ ls *.org(T'org') $ ls *.org(T'org')
@ -193,7 +206,40 @@ I have also had a lot of trouble getting aliases to work, for instance =dired= w
alias less view-file $1 alias less view-file $1
alias d dired $1 alias d dired $1
#+end_src #+end_src
To work around this, I create functions instead.
To work around this, I create functions instead. For instance …
The =basename= shell command strips off a parent, and can strip off the extension. However, you have to specify the extension. Why not take advantage of Emacs =file-name-base= to strip it off without needing to specify it.
#+begin_src emacs-lisp
(defalias 'eshell/base 'file-name-base)
#+end_src
However, the following doesnt work as expected:
#+begin_example
$ set f some-file.org # usually this is from a 'for' loop
$ mv $f {base $f}.txt
#+end_example
While the sequence ={base $f}= works, the =.txt= without a space screws it up, creating a list. So I create this simple function:
#+begin_src emacs-lisp
(defun eshell/newbase (file newbase)
(unless (string-match (rx bos ".") newbase)
(setq newbase (concat "." newbase)))
(concat (file-name-base file) newbase))
#+end_src
Which works as expected:
#+begin_example
$ echo {newbase $f txt}
some-file.txt
#+end_example
A better approach, however, is to take advantage of [[*Predicate Filters and Modifiers][modifiers]].
* Eshell Functions * Eshell Functions
Any function that begins with =eshell/= is available as a command (with the remaining letters). For instance: Any function that begins with =eshell/= is available as a command (with the remaining letters). For instance:
#+begin_src emacs-lisp #+begin_src emacs-lisp
@ -394,15 +440,41 @@ Lets make some test examples:
(should (bufferp (second parms)))))) (should (bufferp (second parms))))))
#+end_src #+end_src
** Setting Variables ** Setting Variables
To set a variable in Eshell, you use good ol =setq=, but that would create global variables. We can make a version for Eshell, that makes buffer-local variables. To set a variable in Eshell, you use good ol =setq=, but that would create global variables. We can make a version for Eshell, that makes buffer-local variables. While we are at it, lets set it as environment variables too. Then everyone can pick it up:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun eshell/set (&rest args) (defun eshell/set (&rest args)
"Creates a buffer local variables." "Creates a buffer local variable.
(dolist (arg-pair (seq-partition args 2)) The first parameters of ARGS is the name of the variable.
(seq-let (var val) arg-pair The other parameters are the values. If not given, the
(let ((var-sym (make-symbol var))) variable is deleted."
(set (make-local-variable var-sym) val))))) (let* ((var (car args))
(var-sym (make-symbol var))
;; Convert value to a string
(val (pcase (seq-length (cdr args))
(0 nil)
(1 (format "%s" (cadr args)))
(_ (thread-last (cdr args)
(seq-map 'eshell-stringify)
(s-join " "))))))
(if val
(progn
(set (make-local-variable var-sym) val)
(setenv var val))
;; If we don't get both a variable and a value, let's try to
;; delete the variable:
(makunbound var-sym)
(setenv var))))
#+end_src #+end_src
Something like:
#+begin_example
$ set a 42 b 7
#+end_example
Would create a single variable, =a=, set to the value of the string, =42 b 7= which is similar to how the Fish shell works.
** Less and More ** Less and More
While I can type =find-file=, I often use =e= as an alias for =emacsclient= in Terminals, so lets do something similar for =eshell=: While I can type =find-file=, I often use =e= as an alias for =emacsclient= in Terminals, so lets do something similar for =eshell=:
Also note that we can take advantage of the =eshell-fn-on-files= function to expand the [[help:find-file][find-file]] (which takes one argument), to open more than one file at one time. Also note that we can take advantage of the =eshell-fn-on-files= function to expand the [[help:find-file][find-file]] (which takes one argument), to open more than one file at one time.
@ -1651,14 +1723,10 @@ Sometimes you need to change something about the current file you are editing...
chmod a+x chmod a+x
Works as expected. We replace the special variable `$$' with the Works as expected. We replace the special variable `$$' with the
filename of the buffer. Note that `eshell-command' executes this filename of the buffer. So:
command, so eshell modifiers are available, for instance:
mv $$ $$(:r).txt mv $$ `basename $$`.txt"
(interactive (list (read-shell-command "Execute command on File Buffer: ")))
Will rename the current file to now have a .txt extension.
See `eshell-display-modifier-help' for details on that."
(interactive "sExecute command on File Buffer: ")
(let* ((file-name (buffer-file-name)) (let* ((file-name (buffer-file-name))
(full-cmd (cond ((string-match (rx "$$") cmd) (full-cmd (cond ((string-match (rx "$$") cmd)
(replace-regexp-in-string (rx "$$") file-name cmd)) (replace-regexp-in-string (rx "$$") file-name cmd))
@ -1667,7 +1735,7 @@ Sometimes you need to change something about the current file you are editing...
(t (t
(concat cmd " " file-name))))) (concat cmd " " file-name)))))
(message "Executing: %s" full-cmd) (message "Executing: %s" full-cmd)
(eshell-command full-cmd))) (shell-command full-cmd)))
#+end_src #+end_src
* Configuration * Configuration
Here is where we associate all the functions and their hooks with =eshell=, through the magic of =use-package=. Here is where we associate all the functions and their hooks with =eshell=, through the magic of =use-package=.