Function: eshell-parse-external-pipeline
eshell-parse-external-pipeline is a byte-compiled function defined in
em-extpipe.el.gz.
Signature
(eshell-parse-external-pipeline)
Documentation
Parse a pipeline intended for execution by the external shell.
A sequence of arguments is rewritten to use the operating system
shell when it contains *|, *< or *>, where the asterisk is
preceded by whitespace or located at the start of input.
The command extends to the next | character which is not
preceded by an unescaped asterisk following whitespace, or the
end of input, except that any Eshell-specific output redirections
occurring at the end are excluded. Any other < or >
appearing before the end of the command are treated as though
preceded by (whitespace and) an asterisk.
For example,
foo <bar *| baz >#<buffer quux>
is equivalent to
sh -c "foo <bar | baz" >#<buffer quux>
when shell-file-name is sh and shell-command-switch is
-c, but in
foo >#<buffer quux> *| baz
and
foo *| baz >#<buffer quux> --some-argument
the Eshell-specific redirect will be passed on to the operating system shell, probably leading to undesired results.
This function must appear early in eshell-parse-argument-hook
to ensure that operating system shell syntax is not interpreted
as though it were Eshell syntax.
Source Code
;; Defined in /usr/src/emacs/lisp/eshell/em-extpipe.el.gz
(defun eshell-parse-external-pipeline ()
"Parse a pipeline intended for execution by the external shell.
A sequence of arguments is rewritten to use the operating system
shell when it contains `*|', `*<' or `*>', where the asterisk is
preceded by whitespace or located at the start of input.
The command extends to the next `|' character which is not
preceded by an unescaped asterisk following whitespace, or the
end of input, except that any Eshell-specific output redirections
occurring at the end are excluded. Any other `<' or `>'
appearing before the end of the command are treated as though
preceded by (whitespace and) an asterisk.
For example,
foo <bar *| baz >#<buffer quux>
is equivalent to
sh -c \"foo <bar | baz\" >#<buffer quux>
when `shell-file-name' is `sh' and `shell-command-switch' is
`-c', but in
foo >#<buffer quux> *| baz
and
foo *| baz >#<buffer quux> --some-argument
the Eshell-specific redirect will be passed on to the operating
system shell, probably leading to undesired results.
This function must appear early in `eshell-parse-argument-hook'
to ensure that operating system shell syntax is not interpreted
as though it were Eshell syntax."
;; Our goal is to wrap the external command to protect it from the
;; other members of `eshell-parse-argument-hook'. We must avoid
;; misinterpreting a quoted `*|', `*<' or `*>' as indicating an
;; external pipeline, hence the structure of the loop in `findbeg1'.
(unless eshell-current-argument-plain
(cl-flet
((findbeg1 (pat &optional go (bound (point-max)))
(let* ((start (point))
(result
(catch 'found
(while (> bound (point))
(let* ((found
(save-excursion
(re-search-forward
"\\(?:#?'\\|\"\\|\\\\\\)" bound t)))
(next (or (and found (match-beginning 0))
bound)))
(if (re-search-forward pat next t)
(throw 'found (match-beginning 1))
(goto-char next)
(while (eshell-extpipe--or-with-catch
(eshell-parse-lisp-argument)
(eshell-parse-backslash)
(eshell-parse-double-quote)
(eshell-parse-literal-quote)))
;; Guard against an infinite loop if none of
;; the parsers moved us forward.
(unless (or (> (point) next) (eobp))
(forward-char 1))))))))
(goto-char (if (and result go) (match-end 0) start))
result)))
(unless (or eshell-current-argument eshell-current-quoted)
(let ((beg (point)) end
(next-marked (findbeg1 "\\(?:\\=\\|\\s-\\)\\(\\*[|<>]\\)"))
(next-unmarked
(or (findbeg1 "\\(?:\\=\\|[^*]\\|\\S-\\*\\)\\(|\\)")
(point-max))))
(when (and next-marked (> next-unmarked next-marked)
(or (> next-marked (point))
(looking-back "\\`\\|\\s-" nil)))
;; Skip to the final segment of the external pipeline.
(while (findbeg1 "\\(?:\\=\\|\\s-\\)\\(\\*|\\)" t))
;; Find output redirections.
(while (findbeg1
"\\([0-9]?>+&?[0-9]?\\s-*\\S-\\)" t next-unmarked)
;; Is the output redirection Eshell-specific? We have our
;; own logic, rather than calling `eshell-parse-argument',
;; to avoid specifying here all the possible cars of
;; parsed special references -- `get-buffer-create' etc.
(forward-char -1)
(let ((this-end
(save-match-data
(cond ((looking-at "#<")
(forward-char 1)
(1+ (eshell-find-delimiter ?\< ?\>)))
((and (looking-at "/\\S-+")
(assoc (match-string 0)
eshell-virtual-targets))
(match-end 0))))))
(cond ((and this-end end)
(goto-char this-end))
(this-end
(goto-char this-end)
(setq end (match-beginning 0)))
(t
(setq end nil)))))
;; We've moved past all Eshell-specific output redirections
;; we could find. If there is only whitespace left, then
;; `end' is right before redirections we should exclude;
;; otherwise, we must include everything.
(unless (and end (skip-syntax-forward "\s" next-unmarked)
(= next-unmarked (point)))
(setq end next-unmarked))
(let ((cmd (string-trim
(buffer-substring-no-properties beg end))))
(goto-char end)
;; We must now drop the asterisks, unless quoted/escaped.
(with-temp-buffer
(insert cmd)
(goto-char (point-min))
(cl-loop
for next = (findbeg1 "\\(?:\\=\\|\\s-\\)\\(\\*[|<>]\\)" t)
while next do (forward-char -2) (delete-char 1))
(eshell-finish-arg
`(eshell-external-pipeline ,(buffer-string)))))))))))