Keep subcommands in pipelines from clobbering the head/tail processes

* lisp/eshell/esh-cmd.el (eshell-execute-pipeline): Use 'make-symbol'
for headproc and tailproc.
(eshell-do-pipelines, eshell-do-pipelines-synchronously): Adapt to the
above.

* test/lisp/eshell/eshell-tests.el (eshell-test/pipe-subcommand)
(eshell-test/pipe-subcommand-with-pipe): New test.

* doc/misc/eshell.texi (Bugs and ideas): Remove item about piping to
process from loop; this commit fixes it (bug#55590).
This commit is contained in:
Jim Porter 2022-05-22 17:27:48 -07:00 committed by Lars Ingebrigtsen
parent be17333ace
commit a49ecdd0ff
3 changed files with 26 additions and 13 deletions

View file

@ -1758,14 +1758,6 @@ alias arg=blah
function arg () @{ blah $* @}
@end example
@item @samp{for i in 1 2 3 @{ grep -q a b && *echo has it @} | wc -l} outputs result after prompt
In fact, piping to a process from a looping construct doesn't work in
general. If I change the call to @code{eshell-copy-handles} in
@code{eshell-rewrite-for-command} to use @code{eshell-protect}, it seems
to work, but the output occurs after the prompt is displayed. The whole
structured command thing is too complicated at present.
@item Pcomplete sometimes gets stuck
You press @key{TAB}, but no completions appear, even though the

View file

@ -827,8 +827,8 @@ This macro calls itself recursively, with NOTFIRST non-nil."
((cdr pipeline) t)
(t (quote 'last)))))
(let ((proc ,(car pipeline)))
(setq headproc (or proc headproc))
(setq tailproc (or tailproc proc))
(set headproc (or proc (symbol-value headproc)))
(set tailproc (or (symbol-value tailproc) proc))
proc))))))
(defmacro eshell-do-pipelines-synchronously (pipeline)
@ -861,7 +861,7 @@ This is used on systems where async subprocesses are not supported."
(let ((result ,(car pipeline)))
;; tailproc gets the result of the last successful process in
;; the pipeline.
(setq tailproc (or result tailproc))
(set tailproc (or result (symbol-value tailproc)))
,(if (cdr pipeline)
`(eshell-do-pipelines-synchronously (quote ,(cdr pipeline))))
result))))
@ -870,7 +870,11 @@ This is used on systems where async subprocesses are not supported."
(defmacro eshell-execute-pipeline (pipeline)
"Execute the commands in PIPELINE, connecting each to one another."
`(let ((eshell-in-pipeline-p t) headproc tailproc)
`(let ((eshell-in-pipeline-p t)
(headproc (make-symbol "headproc"))
(tailproc (make-symbol "tailproc")))
(set headproc nil)
(set tailproc nil)
(progn
,(if (fboundp 'make-process)
`(eshell-do-pipelines ,pipeline)
@ -880,7 +884,8 @@ This is used on systems where async subprocesses are not supported."
(car (aref eshell-current-handles
,eshell-error-handle)) nil)))
(eshell-do-pipelines-synchronously ,pipeline)))
(eshell-process-identity (cons headproc tailproc)))))
(eshell-process-identity (cons (symbol-value headproc)
(symbol-value tailproc))))))
(defmacro eshell-as-subcommand (command)
"Execute COMMAND using a temp buffer.

View file

@ -114,6 +114,22 @@ e.g. \"{(+ 1 2)} 3\" => 3"
(eshell-wait-for-subprocess)
(eshell-match-result "OLLEH\n")))
(ert-deftest eshell-test/pipe-subcommand ()
"Check that piping with an asynchronous subcommand works"
(skip-unless (and (executable-find "echo")
(executable-find "cat")))
(with-temp-eshell
(eshell-command-result-p "echo ${*echo hi} | *cat"
"hi")))
(ert-deftest eshell-test/pipe-subcommand-with-pipe ()
"Check that piping with an asynchronous subcommand with its own pipe works"
(skip-unless (and (executable-find "echo")
(executable-find "cat")))
(with-temp-eshell
(eshell-command-result-p "echo ${*echo hi | *cat} | *cat"
"hi")))
(ert-deftest eshell-test/redirect-buffer ()
"Check that piping to a buffer works"
(with-temp-buffer