unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Sean Whitton <spwhitton@spwhitton.name>
To: 46351@debbugs.gnu.org, Michael Albinus <michael.albinus@gmx.de>
Subject: bug#46351: 28.0.50; Add convenient way to bypass Eshell's own pipelining
Date: Fri, 24 Dec 2021 14:20:27 -0700	[thread overview]
Message-ID: <87tuex1yzo.fsf@melete.silentflame.com> (raw)
In-Reply-To: <87ft26etuh.fsf@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 1134 bytes --]

Hello,

On Mon 08 Feb 2021 at 11:28AM +01, Michael Albinus wrote:

> Maybe we could add it to the defaults. But at least I would
> like to see it documented in the eshell manual, otherwise nobody will
> know the effect of "!!".

I'm attaching new patches which

- switch !! to ||, because !! in shells typically has something to do
  with rerunning the last command

- activate the syntax by default -- I do use Eshell regularly myself,
  and I don't believe that activating it by default is likely to disrupt
  anyone's usage

- adds a section to the Eshell manual introducing the new syntax.

> And I have the impression, that the eshell history is not preserved any
> longer, when I have applied these settings.

I have not been able to reproduce this -- can you confirm?

> It doesn't work for me. The reason is a little bit subtle: you use
> `shell-file-name' in order to call a remote shell. In my case, it is
> "/usr/local/bin/tcsh", which doesn't exist on the remote machine I've
> used for testing.

I was able to fix this by using `with-connection-local-variables'.
Thank you for pointing out the problem.

-- 
Sean Whitton

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: v2-0001-Add-eshell-restore-unexpanded-input.patch --]
[-- Type: text/x-patch, Size: 2720 bytes --]

From d47446cbe7db5c7fb4eba71ab022cfae3de07799 Mon Sep 17 00:00:00 2001
From: Sean Whitton <spwhitton@spwhitton.name>
Date: Sat, 6 Feb 2021 00:01:50 -0700
Subject: [PATCH v2 1/2] Add eshell-restore-unexpanded-input

* lisp/eshell/esh-mode.el (eshell-send-input): Store the original
input before running eshell-expand-input-functions.
* lisp/eshell/esh-mode.el (eshell-restore-unexpanded-input): Define
new function and register as a customization option for
eshell-input-filter-functions.
---
 lisp/eshell/esh-mode.el | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index cae5236d89..87166ef3bf 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -62,6 +62,7 @@
 (require 'esh-module)
 (require 'esh-cmd)
 (require 'esh-arg)                      ;For eshell-parse-arguments
+(require 'cl-lib)
 
 (defgroup eshell-mode nil
   "This module contains code for handling input from the user."
@@ -197,6 +198,7 @@ eshell-first-time-p
 ;; byte-compiler, when compiling other files which `require' this one
 (defvar eshell-mode nil)
 (defvar eshell-command-running-string "--")
+(defvar eshell-last-unexpanded-input nil)
 (defvar eshell-last-input-start nil)
 (defvar eshell-last-input-end nil)
 (defvar eshell-last-output-start nil)
@@ -641,6 +643,7 @@ eshell-send-input
 		(progn
 		  (setq input (buffer-substring-no-properties
 			       eshell-last-output-end (1- (point))))
+                  (setq eshell-last-unexpanded-input input)
 		  (run-hook-with-args 'eshell-expand-input-functions
 				      eshell-last-output-end (1- (point)))
 		  (let ((cmd (eshell-parse-command-input
@@ -674,6 +677,23 @@ eshell-kill-new
 
 (custom-add-option 'eshell-input-filter-functions 'eshell-kill-new)
 
+(defun eshell-restore-unexpanded-input ()
+  "Restore the input text before `eshell-expand-input-functions' ran.
+Useful when you want to see the unexpanded input rather than the
+expanded input in the Eshell buffer, and when you want later
+entries in `eshell-input-filter-functions' to use the unexpanded
+input."
+  (setf (buffer-substring eshell-last-input-start
+                          (1- eshell-last-input-end))
+        eshell-last-unexpanded-input)
+  ;; We have to move point for compatibility with smart display.
+  (save-excursion
+    (goto-char eshell-last-input-end)
+    (eshell-update-markers eshell-last-output-end)))
+
+(custom-add-option 'eshell-input-filter-functions
+                   'eshell-restore-unexpanded-input)
+
 (defun eshell-output-filter (process string)
   "Send the output from PROCESS (STRING) to the interactive display.
 This is done after all necessary filtering has been done."
-- 
2.30.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: v2-0002-Add-eshell-shell-command-and-eshell-expand-to-esh.patch --]
[-- Type: text/x-patch, Size: 7533 bytes --]

From 98df5163018b9c99ad8e65f03422f908c6068c78 Mon Sep 17 00:00:00 2001
From: Sean Whitton <spwhitton@spwhitton.name>
Date: Sat, 6 Feb 2021 00:48:32 -0700
Subject: [PATCH v2 2/2] Add eshell-shell-command and
 eshell-expand-to-eshell-shell-command

* lisp/eshell/esh-mode.el (eshell-shell-command,
eshell-expand-to-eshell-shell-command): Define new functions.
Register eshell-expand-to-eshell-shell-command as a customization
option for eshell-expand-input-functions, and add it by default.
* etc/NEWS:
* doc/misc/eshell.texi: Document the new syntax.
---
 doc/misc/eshell.texi    | 35 ++++++++++++++++++
 etc/NEWS                |  9 +++++
 lisp/eshell/esh-mode.el | 79 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 122 insertions(+), 1 deletion(-)

diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index a87dd4308c..7e9233c09e 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -875,6 +875,7 @@ Expansion
 @menu
 * Dollars Expansion::
 * Globbing::
+* Running Shell Pipelines Natively::
 @end menu
 
 @node Dollars Expansion
@@ -945,6 +946,40 @@ Globbing
 The GNU Emacs Manual}.}
 groups ``eshell-glob'' and ``eshell-pred''.
 
+@node Running Shell Pipelines Natively
+@section Running Shell Pipelines Natively
+When constructing shell pipelines that will move a lot of data, it is
+a good idea to bypass Eshell's own pipelining support and use the
+operating system shell's instead.  This is especially relevant when
+executing commands on a remote machine using Eshell's Tramp
+integration: using the remote shell's pipelining avoids copying the
+data which will flow through the pipeline to local Emacs buffers and
+then right back again.
+
+To quickly construct a command for the native shell, prefix your
+Eshell input with the string @code{||}.  For example,
+
+@example
+|| cat *.ogg | my-cool-decoder >file
+@end example
+
+Executing this command will not copy all the data in the *.ogg files
+into Emacs buffers, as would normally happen with Eshell's own
+pipelining.
+
+As this feature is implemented as an expansion, your original input
+will be replaced with something like this:
+
+@example
+eshell-shell-command \"cat *.ogg | my-cool-decoder >file\"
+@end example
+
+This makes it clear what Eshell is doing, but has the disadvantage of
+being harder to edit, especially for complex pipelines with a lot of
+quoting.  If you would prefer that the @code{||}-prefixed input be
+restored, customize the variable @code{eshell-input-filter-functions}
+to include the function @code{eshell-restore-unexpanded-input}.
+
 @node Input/Output
 @chapter Input/Output
 Since Eshell does not communicate with a terminal like most command
diff --git a/etc/NEWS b/etc/NEWS
index 57fe40c488..767763098b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -749,6 +749,15 @@ the Netscape web browser was released in February, 2008.
 This support has been obsolete since Emacs 25.1.  The final version of
 the Galeon web browser was released in September, 2008.
 
+** Eshell
+
++++
+*** New feature to easily bypass Eshell's own pipelining.
+Prefixing commands with '||' causes them to be executed using the
+operating system shell, which is particularly useful to bypass
+Eshell's own pipelining for pipelines which will move a lot of data.
+See "Running Shell Pipelines Natively" in the Eshell manual.
+
 \f
 * New Modes and Packages in Emacs 29.1
 
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index 87166ef3bf..04680e4305 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -63,6 +63,7 @@
 (require 'esh-cmd)
 (require 'esh-arg)                      ;For eshell-parse-arguments
 (require 'cl-lib)
+(require 'subr-x)
 
 (defgroup eshell-mode nil
   "This module contains code for handling input from the user."
@@ -105,7 +106,8 @@ eshell-send-direct-to-subprocesses
   "If t, send any input immediately to a subprocess."
   :type 'boolean)
 
-(defcustom eshell-expand-input-functions nil
+(defcustom eshell-expand-input-functions
+  '(eshell-expand-to-eshell-shell-command)
   "Functions to call before input is parsed.
 Each function is passed two arguments, which bounds the region of the
 current input text."
@@ -671,6 +673,81 @@ eshell-send-input
 	       (run-hooks 'eshell-post-command-hook)
 	       (insert-and-inherit input)))))))))
 
+(defun eshell-shell-command (command)
+  "Execute COMMAND using the operating system shell."
+  (throw 'eshell-replace-command
+         (with-connection-local-variables
+	  (eshell-parse-command shell-file-name
+			        (list shell-command-switch command)))))
+
+(defun eshell-expand-to-eshell-shell-command (beg end)
+  "Expand Eshell input starting with '||' to use `eshell-shell-command'.
+
+This is intended to provide a convenient way to bypass Eshell's
+own pipelining which can be slow when executing shell pipelines
+which move a lot of data.  It is also useful to avoid
+roundtripping data when running pipelines on remote hosts.
+
+For example, this function expands the input
+
+    || cat *.ogg | my-cool-decoder >file
+
+to
+
+    eshell-shell-command \"cat *.ogg | my-cool-decoder >file\"
+
+Executing the latter command will not copy all the data in the
+*.ogg files into Emacs buffers, as would normally happen with
+Eshell's own pipelining.
+
+This function tries to extract Eshell-specific redirects and
+avoids passing these to the operating system shell.  For example,
+
+    || foo | bar >>#<buffer scratch>
+
+will be expanded to
+
+    eshell-shell-command \"foo | bar\" >>#<buffer scratch>
+
+This function works well in combination with adding
+`eshell-restore-unexpanded-input' to `eshell-input-filter-functions'."
+  (save-excursion
+    (goto-char beg)
+    (when (looking-at "||\\s-*")
+      (let ((end (copy-marker end)) ; needs to be a marker
+            redirects)
+        ;; drop the ||
+        (delete-region beg (match-end 0))
+        ;; extract Eshell-specific redirects
+        (while (search-forward-regexp "[0-9]?>+&?[0-9]?\\s-*\\S-" nil t)
+          (let ((beg (match-beginning 0)))
+            (forward-char -1) ; start from the redirect target
+            (when-let ((end (cond
+                             ;; this is a redirect to a process or a
+                             ;; buffer
+                             ((looking-at "#<")
+                              (forward-char 1)
+                              (1+ (eshell-find-delimiter ?\< ?\>)))
+                             ;; this is a redirect to a virtual target
+                             ((and (looking-at "/\\S-+")
+                                   (assoc (match-string 0)
+                                          eshell-virtual-targets))
+                              (match-end 0)))))
+              (push (buffer-substring-no-properties beg end) redirects)
+              (delete-region beg end)
+              (just-one-space))))
+        ;; wrap the remaining text and reinstate Eshell redirects
+        (let ((pipeline (string-trim
+                         (buffer-substring-no-properties beg end))))
+          (delete-region beg end)
+          (insert "eshell-shell-command ")
+          (prin1 pipeline (current-buffer))
+          (when redirects
+            (insert " " (string-join (nreverse redirects) " "))))))))
+
+(custom-add-option 'eshell-expand-input-functions
+                   'eshell-expand-to-eshell-shell-command)
+
 (defsubst eshell-kill-new ()
   "Add the last input text to the kill ring."
   (kill-ring-save eshell-last-input-start eshell-last-input-end))
-- 
2.30.2


  parent reply	other threads:[~2021-12-24 21:20 UTC|newest]

Thread overview: 60+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-06 20:06 bug#46351: 28.0.50; Add convenient way to bypass Eshell's own pipelining Sean Whitton
2021-02-07  9:17 ` Michael Albinus
2021-02-07 19:01   ` Sean Whitton
2021-02-08 10:28     ` Michael Albinus
2021-02-08 18:07       ` Sean Whitton
2021-02-10 11:33         ` Michael Albinus
2021-12-24 21:20       ` Sean Whitton [this message]
2021-12-25 13:51         ` Michael Albinus
2021-12-25 22:45           ` Sean Whitton
2021-12-27 14:42             ` Michael Albinus
2021-12-27 18:13               ` Sean Whitton
2021-12-27 18:22                 ` Eli Zaretskii
2021-12-27 19:21                   ` Sean Whitton
2021-12-27 19:35                     ` Eli Zaretskii
2021-12-27 19:53                       ` Sean Whitton
2021-12-27 19:37                     ` Michael Albinus
2021-12-27 19:54                       ` Sean Whitton
2021-12-28  8:58                         ` Michael Albinus
2022-01-18  5:19                           ` Sean Whitton
2022-01-18  9:49                             ` Robert Pluim
2022-01-18 18:27                               ` Sean Whitton
2022-01-18 14:45                             ` Eli Zaretskii
2022-01-18 18:40                               ` Sean Whitton
2022-01-18 19:38                                 ` Eli Zaretskii
2022-01-18 23:16                                   ` Sean Whitton
2022-01-19  7:34                                     ` Eli Zaretskii
2022-01-19 20:39                                       ` Sean Whitton
2022-01-20  6:53                                         ` Eli Zaretskii
2022-01-20 22:16                                           ` Sean Whitton
2022-01-21  6:54                                             ` Eli Zaretskii
2022-01-22  0:16                                               ` Sean Whitton
2022-01-18 18:42                               ` Sean Whitton
2022-01-19 15:52                             ` Michael Albinus
2022-01-19 20:54                               ` Sean Whitton
2022-01-20 18:41                                 ` Michael Albinus
2022-01-20 22:17                                   ` Sean Whitton
2022-01-23 22:39                             ` Sean Whitton
2022-01-24  9:55                               ` Lars Ingebrigtsen
2022-01-24 14:18                               ` Michael Albinus
2022-01-24 20:32                                 ` Sean Whitton
2022-01-24 20:44                                   ` Sean Whitton
2022-01-24 20:48                                     ` Lars Ingebrigtsen
2022-01-24 21:42                                       ` Sean Whitton
2022-01-24 21:51                                         ` Lars Ingebrigtsen
2022-01-24 22:48                                           ` Sean Whitton
2022-01-24 20:46                                   ` Lars Ingebrigtsen
2022-01-25  2:39                                     ` Jim Porter
2022-01-25  5:33                                       ` bug#53518: 29.0.50; em-extpipe breaks input of sharp-quoted Lisp symbols Sean Whitton
2022-01-25  8:50                                       ` Sean Whitton
2022-01-25 12:26                                         ` Lars Ingebrigtsen
2022-01-25 16:48                                           ` Sean Whitton
2022-01-26  5:38                                             ` Jim Porter
2022-01-26 13:13                                             ` Lars Ingebrigtsen
2022-01-25 18:14                                         ` Jim Porter
2022-01-25 20:01                                           ` Sean Whitton
2022-01-25 20:52                                             ` Jim Porter
2022-01-25 22:38                                               ` Sean Whitton
2022-01-25  8:54                                       ` bug#46351: 28.0.50; Add convenient way to bypass Eshell's own pipelining Michael Albinus
2022-01-25 18:22                                         ` Jim Porter
2021-12-27 18:26                 ` Michael Albinus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87tuex1yzo.fsf@melete.silentflame.com \
    --to=spwhitton@spwhitton.name \
    --cc=46351@debbugs.gnu.org \
    --cc=michael.albinus@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).