unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [RFC] Explicity-remote commands in Eshell
@ 2023-03-06  2:35 Jim Porter
  2023-03-06  8:09 ` Michael Albinus
  0 siblings, 1 reply; 4+ messages in thread
From: Jim Porter @ 2023-03-06  2:35 UTC (permalink / raw)
  To: emacs-devel; +Cc: michael.albinus

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

One of the nice features of Eshell is that since it's aware of Tramp, 
connecting to a different machine (or changing users on a machine) is 
just a matter of cd'ing into a remote directory:

   cd /ssh:user@somewhere:~/some/dir

In addition, Eshell routes data in pipelines through buffers. In 
principle, this means it should be easy to build cross-host pipelines in 
Eshell, where the output of a command on host A is piped into the input 
of a command on host B. However, it's not easy to spell this in Eshell; 
the best I can come up with is to use a subshell like this:

   {cd /ssh:user@somewhere: && remote-command} | local-command

Instead, I think we could make this easier:

   /ssh:user@somewhere:remote-command | local-command

What do people think of the above syntax? It's a little bit different 
from the usual Tramp syntax, since the local part shouldn't be expanded 
using 'expand-file-name'; it's an executable somewhere on the remote 
$PATH. I can't think of any problems with supporting this syntax in 
Eshell[1], but I'm interested to hear others' thoughts.

Attached is a patch demonstrating this. The only non-Eshell change I had 
to make was to enhance 'file-remote-p' so that I can tell Tramp not to 
call 'expand-file-name'.

As a footnote: you might wonder how to execute a program on your local 
host when the current Eshell directory is remote. In my patch, I chose 
to do this via quoted file names:

   /:local-command

[1] Tramp shouldn't need to know about this syntax in other places. It 
can just be something Eshell understands.

[-- Attachment #2: 0001-Add-support-for-explicitly-remote-commands-in-Eshell.patch --]
[-- Type: text/plain, Size: 6000 bytes --]

From 676659ebcbf2cabc5e606620fdd58e3f498225f9 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 22 Jan 2023 22:45:40 -0800
Subject: [PATCH] Add support for explicitly-remote commands in Eshell

* lisp/files.el (file-remote-p): Document 'never' for CONNECTED
argument.

* lisp/net/tramp.el (tramp-handle-file-remote-p): Handle CONNECTED
value of 'never'.

* lisp/eshell/esh-ext.el (eshell-explicit-remote-commands): New
option.
(eshell-ext-initialize): Apply 'eshell-handle-remote-command' when
requested.
(eshell-handle-remote-command): New function.
(eshell-remote-command): Reimplement this function and dispatch to
'eshell-external-command', which can handle remote processes on its
own.
---
 lisp/eshell/esh-ext.el | 58 ++++++++++++++++++++++++++----------------
 lisp/files.el          |  4 ++-
 lisp/net/tramp.el      |  5 ++--
 3 files changed, 42 insertions(+), 25 deletions(-)

diff --git a/lisp/eshell/esh-ext.el b/lisp/eshell/esh-ext.el
index f350622e78c..d720cde0dd2 100644
--- a/lisp/eshell/esh-ext.el
+++ b/lisp/eshell/esh-ext.el
@@ -168,11 +168,23 @@ eshell-explicit-command-char
   :type 'character
   :group 'eshell-ext)
 
+(defcustom eshell-explicit-remote-commands t
+  "If non-nil, support explicitly-remote commands.
+These are commands with a full remote file name, such as
+\"/ssh:host:whoami\".  If this is enabled, you can also run
+explicitly-local commands by using a quoted file name, like
+\"/:whoami\"."
+  :type 'boolean
+  :group 'eshell-ext)
+
 ;;; Functions:
 
 (defun eshell-ext-initialize ()     ;Called from `eshell-mode' via intern-soft!
   "Initialize the external command handling code."
-  (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t))
+  (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t)
+  (when eshell-explicit-remote-commands
+    (add-hook 'eshell-named-command-hook
+              #'eshell-handle-remote-command nil t)))
 
 (defun eshell-explicit-command (command args)
   "If a command name begins with `*', call it externally always.
@@ -186,30 +198,32 @@ eshell-explicit-command
 	(error "%s: external command not found"
 	       (substring command 1))))))
 
+(defun eshell-handle-remote-command (command args)
+  "Handle remote (or quoted) COMMAND names, using ARGS.
+This calls the appropriate function for commands that aren't on
+the connection associated with `default-directory'.  (See
+`eshell-explicit-remote-commands'.)"
+  (if (file-name-quoted-p command)
+      (let ((default-directory (if (file-remote-p default-directory)
+                                   (expand-file-name "~")
+                                 default-directory)))
+        (eshell-external-command (file-name-unquote command) args))
+    (when (file-remote-p command)
+      (eshell-remote-command command args))))
+
 (defun eshell-remote-command (command args)
   "Insert output from a remote COMMAND, using ARGS.
 A remote command is something that executes on a different machine.
-An external command simply means external to Emacs.
-
-Note that this function is very crude at the moment.  It gathers up
-all the output from the remote command, and sends it all at once,
-causing the user to wonder if anything's really going on..."
-  (let ((outbuf (generate-new-buffer " *eshell remote output*"))
-	(errbuf (generate-new-buffer " *eshell remote error*"))
-	(command (file-local-name command))
-	(exitcode 1))
-    (unwind-protect
-	(progn
-	  (setq exitcode
-		(shell-command
-		 (mapconcat #'shell-quote-argument
-			    (append (list command) args) " ")
-		 outbuf errbuf))
-	  (eshell-print (with-current-buffer outbuf (buffer-string)))
-	  (eshell-error (with-current-buffer errbuf (buffer-string))))
-      (eshell-close-handles exitcode 'nil)
-      (kill-buffer outbuf)
-      (kill-buffer errbuf))))
+An external command simply means external to Emacs."
+  (let* ((cwd-connection (file-remote-p default-directory))
+         (command-connection (file-remote-p command))
+         (default-directory (if (equal cwd-connection command-connection)
+                                default-directory
+                              command-connection))
+         (command-localname (file-remote-p command 'localname 'never)))
+    (unless command-connection
+      (error "%s: not a remote command" command))
+    (eshell-external-command command-localname args)))
 
 (defun eshell-external-command (command args)
   "Insert output from an external COMMAND, using ARGS."
diff --git a/lisp/files.el b/lisp/files.el
index 387a3b5dc66..e5cb775eb6d 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1267,7 +1267,9 @@ file-remote-p
 
 If CONNECTED is non-nil, return an identification only if FILE is
 located on a remote system and a connection is established to
-that remote system.
+that remote system.  If CONNECTED is `never', never use an
+existing connection to return the identification (this is
+otherwise like a value of nil).
 
 Tip: You can use this expansion of remote identifier components
      to derive a new remote file name from an existing one.  For
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 47173b95bea..e2b0e6bffa0 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -4269,13 +4269,14 @@ tramp-handle-file-remote-p
   (let ((tramp-verbose (min tramp-verbose 3)))
     (when (tramp-tramp-file-p filename)
       (let* ((o (tramp-dissect-file-name filename))
-	     (p (tramp-get-connection-process o))
+	     (p (and (not (eq connected 'never))
+                     (tramp-get-connection-process o)))
 	     (c (and (process-live-p p)
 		     (tramp-get-connection-property p "connected"))))
 	;; We expand the file name only, if there is already a connection.
 	(with-parsed-tramp-file-name
 	    (if c (expand-file-name filename) filename) nil
-	  (and (or (not connected) c)
+	  (and (or (memq connected '(nil never)) c)
 	       (cond
 		((eq identification 'method) method)
 		;; Domain and port are appended to user and host,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC] Explicity-remote commands in Eshell
  2023-03-06  2:35 [RFC] Explicity-remote commands in Eshell Jim Porter
@ 2023-03-06  8:09 ` Michael Albinus
  2023-03-06 16:10   ` John Wiegley
  2023-03-06 22:07   ` Jim Porter
  0 siblings, 2 replies; 4+ messages in thread
From: Michael Albinus @ 2023-03-06  8:09 UTC (permalink / raw)
  To: Jim Porter; +Cc: emacs-devel

Jim Porter <jporterbugs@gmail.com> writes:

Hi Jim,

> Instead, I think we could make this easier:
>
>   /ssh:user@somewhere:remote-command | local-command
>
> What do people think of the above syntax? It's a little bit different
> from the usual Tramp syntax, since the local part shouldn't be
> expanded using 'expand-file-name'; it's an executable somewhere on the
> remote $PATH. I can't think of any problems with supporting this
> syntax in Eshell[1], but I'm interested to hear others' thoughts.

I've skimmed over the patch, and there's nothing to object from Tramp
pov. I'm not a regular Eshell user, so I don't know how useful it will
be.

One remark: Why don't you allow remoteness everywhere? Sth like

--8<---------------cut here---------------start------------->8---
/ssh:user@somewhere:remote-command | /ssh:another-user@elsewhere:another-remote-command
--8<---------------cut here---------------end--------------->8---

This even with multiple pipes in a row. Mixed with local commands. And
using just the shorter version "command", which means a local or remote
command according to default-directory.

"/:command" is a nice trick, btw :-)

Best regards, Michael.



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC] Explicity-remote commands in Eshell
  2023-03-06  8:09 ` Michael Albinus
@ 2023-03-06 16:10   ` John Wiegley
  2023-03-06 22:07   ` Jim Porter
  1 sibling, 0 replies; 4+ messages in thread
From: John Wiegley @ 2023-03-06 16:10 UTC (permalink / raw)
  To: Michael Albinus; +Cc: Jim Porter, emacs-devel

>>>>> "MA" == Michael Albinus <michael.albinus@gmx.de> writes:

MA> I've skimmed over the patch, and there's nothing to object from Tramp pov.
MA> I'm not a regular Eshell user, so I don't know how useful it will be.

It looks like it could be useful to me, only needing a subtle addition to the
syntax...

-- 
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC] Explicity-remote commands in Eshell
  2023-03-06  8:09 ` Michael Albinus
  2023-03-06 16:10   ` John Wiegley
@ 2023-03-06 22:07   ` Jim Porter
  1 sibling, 0 replies; 4+ messages in thread
From: Jim Porter @ 2023-03-06 22:07 UTC (permalink / raw)
  To: Michael Albinus; +Cc: emacs-devel

On 3/6/2023 12:09 AM, Michael Albinus wrote:
> One remark: Why don't you allow remoteness everywhere? Sth like
> 
> --8<---------------cut here---------------start------------->8---
> /ssh:user@somewhere:remote-command | /ssh:another-user@elsewhere:another-remote-command
> --8<---------------cut here---------------end--------------->8---
> 
> This even with multiple pipes in a row. Mixed with local commands. And
> using just the shorter version "command", which means a local or remote
> command according to default-directory.

This is possible too: in fact, explicitly-remote commands should be 
usable absolutely anywhere a regular command is: in pipelines, 
subshells, conditionals, etc. (My example was just to show how you might 
mix an explicitly-remote command with a regular command.)



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-03-06 22:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-06  2:35 [RFC] Explicity-remote commands in Eshell Jim Porter
2023-03-06  8:09 ` Michael Albinus
2023-03-06 16:10   ` John Wiegley
2023-03-06 22:07   ` Jim Porter

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).