GNU bug report logs -
#59469
29.0.50; Eshell "for" loop: Calling a non-lisp command (example: /usr/bin/tail) sets the variable exported in the {} block of "for var in list {}" to nil
Previous Next
Full log
Message #17 received at 59469 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On 11/21/2022 11:18 PM, Milan Zimmermann wrote:
> But it sounds to me like your intuition about this could be fixed by
> rewriting the core 'eshell-do-eval' loop in bug#57635 can be correct. I
> would enjoy helping with it, but at the moment it is above my time and
> elisp abilities.
After digging through 'eshell-do-eval' for another issue, I think I
mostly understand it now. I still think bug#57635 is the way to go
long-term (either that or use real threads), but since that's a big
change, it might be best to give users some time where they could opt
out of some new Eshell evaluation backend, just in case we break
something with it. As such, I want to make sure the existing backend is
as bug-free as possible so that (ideally) switching between the two has
no behavioral difference.
With that said, here's a patch that fixes this, plus a regression test.
One question for anyone reviewing the patch: is there a better way to do
the "catch and rethrow" dance I do in 'eshell-do-eval'? It seems kind of
clumsy...
----------------------------------------
The patch has a description of the problem, but I'll describe it in more
detail here for anyone curious. Eshell turns the command in the original
message into a form like this (note: this is very approximate):
(let ((process-environment process-environment))
(setenv "var" "value")
(eshell-external-command "grep") ; This throws 'eshell-defer'
(eshell/echo (getenv "var")))
'eshell-do-eval' gradually steps through this form, evaluating subforms
and replacing them with their (quoted) results. This way, when a command
throws 'eshell-defer', you can resume this form simply by calling
'eshell-do-eval' again. So at the point 'eshell-defer' gets thrown, the
form has been updated to:
(let ((process-environment process-environment))
'"value" ; The quoted result of 'setenv' above.
(eshell-external-command "grep")
(eshell/echo (getenv "var")))
But since 'process-environment' is let-bound, when we resume evaluation,
it's as though 'setenv' had never been called at all!
The fix here is that when we're inside a 'let' and see 'eshell-defer'
get thrown, update the let-bindings in place. So now the updated form
would look like:
(let ((process-environment (cons "var=value" process-environment)))
'"value" ; Not really necessary, but it doesn't hurt anything.
(eshell-external-command "grep")
(eshell/echo (getenv "var")))
And so with this, it all works.
[0001-Ensure-that-deferred-commands-don-t-make-Eshell-forg.patch (text/plain, attachment)]
This bug report was last modified 2 years and 197 days ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.