From: Jim Porter Date: Sat, 15 Jun 2024 03:45:44 +0000 (-0700) Subject: Allow specifying stdout/stderr separately in some Eshell commands X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=6f474e4d8e96c97066cd519a2d7287e0712635cb;p=emacs.git Allow specifying stdout/stderr separately in some Eshell commands * lisp/eshell/eshell.el (eshell-command): Add ERROR-TARGET. * lisp/eshell/em-script.el (eshell-execute-file): Make interactive, and add ERROR-TARGET. * doc/misc/eshell.texi (One-Off Commands, Scripts): Update documentation. * etc/NEWS: Announce this change. (cherry picked from commit 99b360bb5aabf324cf038c27ac76ac1513319754) --- diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 5897947157b..4416477c60f 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -235,11 +235,19 @@ Start a new Eshell session, no matter if another one already exists. You can also run individual Eshell commands from anywhere within Emacs: -@deffn Command eshell-command command &optional to-current-buffer +@deffn Command eshell-command command &optional output-target error-target Execute the Eshell command string @var{command} and show the output in a -buffer. If @var{to-current-buffer} is non-@code{nil} (interactively, -with the prefix argument), then insert output into the current buffer at -point. +buffer. If @var{output-target} is @code{t} (interactively, with the +prefix argument), write the command's standard output to the current +buffer at point. If @code{nil}, write the output to a new output +buffer. For any other value, output to that Eshell target +(@pxref{Redirection}). + +@var{error-target} is similar to @var{output-target}, except that it +controls where to write standard error, and a @code{nil} value means to +write standard error to the same place as standard output. (To suppress +standard error, you can write to the Eshell virtual target +@file{/dev/null}.) When the command ends with @kbd{&}, Eshell will evaluate the command asynchronously. Otherwise, it will wait until the command has finished @@ -275,13 +283,20 @@ the special variables @code{$0}, @code{$1}, @dots{}, @code{$9}, and You can also invoke Eshell scripts from outside of Eshell: -@defun eshell-execute-file file &optional args destination +@deffn Command eshell-execute-file file &optional args output-target error-target Execute the Eshell commands contained in @var{file}, passing an optional -list of @var{args} to the script. If @var{destination} is @code{t}, -write the command output to the current buffer. If @code{nil}, don't -write the output anywhere. For any other value, output to the -corresponding Eshell target (@pxref{Redirection}). -@end defun +list of @var{args} to the script. If @var{output-target} is @code{t} +(interactively, with the prefix argument), write the command output to +the current buffer. If @code{nil}, don't write the output anywhere. +For any other value, output to the corresponding Eshell target +(@pxref{Redirection}). + +@var{error-target} is similar to @var{output-target}, except that it +controls where to write standard error, and a @code{nil} value means to +write standard error to the same place as standard output. (To suppress +standard error, you can write to the Eshell virtual target +@file{/dev/null\}.) +@end deffn @cindex batch scripts @defun eshell-batch-file diff --git a/etc/NEWS b/etc/NEWS index f10f9ae4d65..3ca90720390 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -54,6 +54,18 @@ this will prompt for confirmation before creating a new buffer when necessary. To restore the previous behavior, set this option to 'confirm-kill-process'. ++++ +*** 'eshell-execute-file' is now an interactive command. +Interactively, this now prompts for a script file to execute. With the +prefix argument, it will also insert any output into the current buffer +at point. + ++++ +*** 'eshell-command' and 'eshell-execute-file' can now set where stderr goes. +These functions now take an optional ERROR-TARGET argument to control +where to send the standard error output. See the "(eshell) Entry +Points" node in the Eshell manual for more details. + +++ *** Eshell's built-in "wait" command now accepts a timeout. By passing "-t" or "--timeout", you can specify a maximum time to wait diff --git a/lisp/eshell/em-script.el b/lisp/eshell/em-script.el index ba020d2eb5b..80dea16106b 100644 --- a/lisp/eshell/em-script.el +++ b/lisp/eshell/em-script.el @@ -106,20 +106,29 @@ Comments begin with `#'." (eshell--source-file file args subcommand-p))) ;;;###autoload -(defun eshell-execute-file (file &optional args destination) +(defun eshell-execute-file (file &optional args output-target error-target) "Execute a series of Eshell commands in FILE, passing ARGS. -If DESTINATION is t, write the command output to the current buffer. If -nil, don't write the output anywhere. For any other value, output to -the corresponding Eshell target (see `eshell-get-target'). +If OUTPUT-TARGET is t (interactively, with the prefix argument), write +the command's standard output to the current buffer at point. If nil, +don't write the output anywhere. For any other value, output to that +Eshell target (see `eshell-get-target'). + +ERROR-TARGET is similar to OUTPUT-TARGET, except that it controls where +to write standard error, and a nil value means to write standard error +to the same place as standard output. (To suppress standard error, you +can write to the Eshell virtual target \"/dev/null\".) Comments begin with `#'." + (interactive (list (read-file-name "Execute file: " nil nil t) + nil (not (not current-prefix-arg)))) (let ((eshell-non-interactive-p t) - (stdout (if (eq destination t) (current-buffer) destination))) + (stdout (if (eq output-target t) (current-buffer) output-target)) + (stderr (if (eq error-target t) (current-buffer) error-target))) (with-temp-buffer (eshell-mode) (eshell-do-eval `(let ((eshell-current-handles - (eshell-create-handles ,stdout 'insert)) + (eshell-create-handles ,stdout 'insert ,stderr 'insert)) (eshell-current-subjob-p)) ,(eshell--source-file file args)) t)))) diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el index d60101d51e1..6637ff36a2c 100644 --- a/lisp/eshell/eshell.el +++ b/lisp/eshell/eshell.el @@ -327,31 +327,42 @@ information on Eshell, see Info node `(eshell)Top'." (defvar eshell-command-buffer-name-sync "*Eshell Command Output*") ;;;###autoload -(defun eshell-command (command &optional to-current-buffer) +(defun eshell-command (command &optional output-target error-target) "Execute the Eshell command string COMMAND. -If TO-CURRENT-BUFFER is non-nil (interactively, with the prefix -argument), then insert output into the current buffer at point. - -When \"&\" is added at end of command, the command is async and its output -appears in a specific buffer. You can customize +If OUTPUT-TARGET is t (interactively, with the prefix argument), write +the command's standard output to the current buffer at point. If nil, +write the output to a new output buffer. For any other value, output to +that Eshell target (see `eshell-get-target'). + +ERROR-TARGET is similar to OUTPUT-TARGET, except that it controls where +to write standard error, and a nil value means to write standard error +to the same place as standard output. (To suppress standard error, you +can write to the Eshell virtual target \"/dev/null\".) + +When \"&\" is added at end of command, the command is async and its +output appears in a specific buffer. You can customize `eshell-command-async-buffer' to specify what to do when this output buffer is already taken by another running shell command." (interactive (list (eshell-read-command) - current-prefix-arg)) + (not (not current-prefix-arg)))) (save-excursion - (let ((stdout (if to-current-buffer (current-buffer) t)) + (let ((stdout (cond ((eq output-target t) (current-buffer)) + ((not output-target) t) + (t output-target))) + (stderr (if (eq error-target t) (current-buffer) error-target)) (buf (set-buffer (generate-new-buffer " *eshell cmd*"))) (eshell-non-interactive-p t)) (eshell-mode) (let* ((proc (eshell-eval-command `(let ((eshell-current-handles - (eshell-create-handles ,stdout 'insert)) + (eshell-create-handles ,stdout 'insert + ,stderr 'insert)) (eshell-current-subjob-p)) ,(eshell-parse-command command)) command)) (async (eq (car-safe proc) :eshell-background)) (bufname (cond - (to-current-buffer nil) + ((not (eq stdout t)) nil) (async eshell-command-buffer-name-async) (t eshell-command-buffer-name-sync))) unique) @@ -394,7 +405,7 @@ buffer is already taken by another running shell command." (while (and (bolp) (not (bobp))) (delete-char -1))) (cl-assert (and buf (buffer-live-p buf))) - (unless to-current-buffer + (unless bufname (let ((len (if async 2 (count-lines (point-min) (point-max))))) (cond