reddit / emacs
Eshell aliases for mv and cp using their elisp version don't work.
Silovikigov · 2022-09-30 14:23
I'm trying to make use of the `--interactive` and `--verbose` flags for mv and cp in Eshell.
I have an issue with the argument(s) to add at the end either it is $*, $1, $2 etc.
When I have: `alias mv eshell/mv --interactive --verbose $1 $2` it works for moving a single file but obviously fails when I'm trying to do `mv foo1 foo2 foo3 bar/` (same story for cp).
So the question is: how can I use the Eshell version of mv/cp with an alias in the Eshell alias file?
big_lentil · 2022-09-30 15:05
Maybe try
alias mv 'eshell/mv --interactive --verbose $*'
I don't use eshell but I just checked its info manual and the example given there is ‘alias ll 'ls -l $*'’, so this should hopefully work
Silovikigov · 2022-09-30 15:58
it returns an error: `mv: missing destination file or directory` when using `$*` but works fine with `ls` as your example.
noman_032018 · 2022-09-30 16:46
While not using the alias mechanism, it might be an option to just [:around advise the function](https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html) to add the arguments in front of the argument list each time.
Some of the drawbacks that'd apply to advising more general functions or functions you don't always want modified don't really apply in your case.
big_lentil · 2022-09-30 16:58
I looked into it a bit more and this is what I'm seeing after turning toggle-debug-on-error on
(eshell/mv ("--interactive" "--verbose" ("foo" "bar")))
Looks like the source of the error is $* in aliases passing args as a list of strings as in ("x" "y") instead of just "x y".
Not really sure how to handle that as trying to use $* inside elisp code within an alias doesn't seem to work.
Edit: OK so I got too frustrated to leave this alone and ended up with below solution:
Define the following in your init.el (preferably with a better name)
(defun foo (&rest args)
(let ((x (append (list "--interactive" "--verbose") (elt args 0))))
(apply #'eshell/mv x)))
Then define the following alias in eshell
alias mv 'foo $*'
This just defines a wrapper function to properly handle the arguments passed by $*, so it ends up being the same idea as an advice
Silovikigov · 2022-09-30 17:39
I tried `(advice-add 'eshell/mv :around "--interactive --verbose")` but I'm getting `Invalid function: "--interactive --verbose"` error.
how do you advice-add this properly?
noman_032018 · 2022-09-30 17:45
Ah, it's a bit more complicated than that. Advising uses functions to modify the actions of other functions.
In this case an `:around` function receives the original function as its first argument, then all the rest of the arguments as a `&rest` list as shown in the `his-tracing-function` example in [the documentation](https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html) so that it can wrap *around* the original function and modify its arguments & environment as desired (it is also responsible for calling the original function passed as an argument within itself).
I'm not quite sure how Eshell passes arguments to functions, so you would have to use [edebug](https://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html) and stepping or breakpoints to figure that out. Or I suppose you could just have your first version of the `:around` function throw an error in a way that allows `toggle-debug-on-error`'s enabled error popups to tell you how the arguments are shaped.
I suspect the [sequence functions](https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html) might come in handy for then passing the modified arguments to a `funcall` (or `apply`, it's up to you) of `orig-func`.
Note: You can use another pattern than the `&rest` list for the arguments, but it's more versatile and reliable to do it the way the example shows, in the event where the wrapped function might be modified in the future to take more or less arguments (usually ends up being more optional ones). Given the function you want to modify is `mv`, I would imagine it already takes an arbitrarily-long list of arguments anyway.
Silovikigov · 2022-09-30 21:06
Thank you!
I tested several use cases on mv and cp and everything works as expected. Wish I was an elisp wizard myself sometimes.
>Looks like the source of the error is $* in aliases passing args as a list of strings as in ("x" "y") instead of just "x y".
Do you think this is worth being reported as a bug to the emacs dev?
noman_032018 · 2022-09-30 21:14
It is at the very least a bug in the documentation.
Silovikigov · 2022-09-30 21:33
thanks a lot for the thorough explanation, this is a lot to grasp for me and warp my head around for now
also I was looking for defcustom options on eshell functions and I found those:
```
(eshell-rm-remove-directories)
(eshell-rm-interactive-query)
(eshell-mv-overwrite-files)
(eshell-mv-interactive-query)
(eshell-cp-interactive-query)
(eshell-cp-overwrite-files)
```
looks like not all possible flags can be toggled on or off though.
noman_032018 · 2022-09-30 21:42
Yeah, those customizable variables serving as flags depend on the programmer having both used them and described the type adequately for the customization system to handle them. Otherwise you end-up having to use Elisp to modify them, which can be a bit more complicated in larger projects as [they can use non-default custom setters](https://www.gnu.org/software/emacs/manual/html_node/elisp/Applying-Customizations.html).
The other solution provided by u/big_lentil is mostly equivalent to what I was suggesting (minus more complicated advising interactions, but you don't have to care about them here).