>From d5d48427c75555cd5cf2f6eb5b89e0c832cf7edf Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 6 Feb 2021 00:48:32 -0700 Subject: [PATCH 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. --- lisp/eshell/esh-mode.el | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 6036829941..b3132393a5 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 'subr-x) (defgroup eshell-mode nil "This module contains code for handling input from the user." @@ -660,6 +661,82 @@ newline." (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 + (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. + +To enable, add this function to `eshell-expand-input-functions'. + +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 >># + +will be expanded to + + eshell-shell-command \"foo | bar\" >># + +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.29.2