Fix Eshell dollar interpolation inside of double-quotes

For example,

  echo "${echo hi}"

previously tried to run the program named 'echo hi', instead of 'echo'
with the argument 'hi'.

* lisp/eshell/esh-arg.el (eshell-parse-inner-double-quote):
New function.

* lisp/eshell/esh-var.el (eshell-parse-variable-ref): Support parsing
when wrapped in double-quiotes.

* test/lisp/eshell/esh-var-tests.el (esh-var-test/interp-var)
(esh-var-test/interp-quoted-var)
(esh-var-test/interp-quoted-var-concat)
(esh-var-test/quoted-interp-var)
(esh-var-test/quoted-interp-quoted-var)
(esh-var-test/quoted-interp-lisp, esh-var-test/quoted-interp-cmd)
(esh-var-test/quoted-interp-temp-cmd): New tests.
This commit is contained in:
Jim Porter 2022-02-27 21:04:30 -08:00 committed by Lars Ingebrigtsen
parent ae1acb6017
commit cccee7e840
3 changed files with 91 additions and 9 deletions

View file

@ -354,6 +354,30 @@ after are both returned."
(list 'eshell-escape-arg arg))))
(goto-char (1+ end)))))))
(defun eshell-parse-inner-double-quote (bound)
"Parse the inner part of a double quoted string.
The string to parse starts at point and ends at BOUND.
If Eshell is currently parsing a quoted string and there are any
backslash-escaped characters, this will return the unescaped
string, updating point to BOUND. Otherwise, this returns nil and
leaves point where it was."
(when eshell-current-quoted
(let (strings
(start (point))
(special-char
(rx-to-string
`(seq "\\" (group (any ,@eshell-special-chars-inside-quoting))))))
(while (re-search-forward special-char bound t)
(push (concat (buffer-substring start (match-beginning 0))
(match-string 1))
strings)
(setq start (match-end 0)))
(when strings
(push (buffer-substring start bound) strings)
(goto-char bound)
(apply #'concat (nreverse strings))))))
(defun eshell-parse-special-reference ()
"Parse a special syntax reference, of the form `#<args>'.

View file

@ -440,18 +440,16 @@ Possible options are:
(let ((end (eshell-find-delimiter ?\{ ?\})))
(if (not end)
(throw 'eshell-incomplete ?\{)
(forward-char)
(prog1
`(eshell-convert
(eshell-command-to-value
(eshell-as-subcommand
,(eshell-parse-command (cons (1+ (point)) end)))))
,(let ((subcmd (or (eshell-parse-inner-double-quote end)
(cons (point) end)))
(eshell-current-quoted nil))
(eshell-parse-command subcmd)))))
(goto-char (1+ end))))))
((memq (char-after) '(?\' ?\"))
(let ((name (if (eq (char-after) ?\')
(eshell-parse-literal-quote)
(eshell-parse-double-quote))))
(if name
`(eshell-get-variable ,(eval name) indices))))
((eq (char-after) ?\<)
(let ((end (eshell-find-delimiter ?\< ?\>)))
(if (not end)
@ -463,7 +461,9 @@ Possible options are:
`(let ((eshell-current-handles
(eshell-create-handles ,temp 'overwrite)))
(progn
(eshell-as-subcommand ,(eshell-parse-command cmd))
(eshell-as-subcommand
,(let ((eshell-current-quoted nil))
(eshell-parse-command cmd)))
(ignore
(nconc eshell-this-command-hook
;; Quote this lambda; it will be evaluated
@ -478,9 +478,18 @@ Possible options are:
(condition-case nil
`(eshell-command-to-value
(eshell-lisp-command
',(read (current-buffer))))
',(read (or (eshell-parse-inner-double-quote (point-max))
(current-buffer)))))
(end-of-file
(throw 'eshell-incomplete ?\())))
((looking-at (rx (or "'" "\"" "\\\"")))
(eshell-with-temp-command (or (eshell-parse-inner-double-quote (point-max))
(cons (point) (point-max)))
(let ((name (if (eq (char-after) ?\')
(eshell-parse-literal-quote)
(eshell-parse-double-quote))))
(when name
`(eshell-get-variable ,(eval name) indices)))))
((assoc (char-to-string (char-after))
eshell-variable-aliases-list)
(forward-char)

View file

@ -37,6 +37,25 @@
;; Variable interpolation
(ert-deftest esh-var-test/interp-var ()
"Interpolate variable"
(should (equal (eshell-test-command-result "echo $user-login-name")
user-login-name)))
(ert-deftest esh-var-test/interp-quoted-var ()
"Interpolate quoted variable"
(should (equal (eshell-test-command-result "echo $'user-login-name'")
user-login-name))
(should (equal (eshell-test-command-result "echo $\"user-login-name\"")
user-login-name)))
(ert-deftest esh-var-test/interp-quoted-var-concat ()
"Interpolate and concat quoted variable"
(should (equal (eshell-test-command-result "echo $'user-login-name'-foo")
(concat user-login-name "-foo")))
(should (equal (eshell-test-command-result "echo $\"user-login-name\"-foo")
(concat user-login-name "-foo"))))
(ert-deftest esh-var-test/interp-lisp ()
"Interpolate Lisp form evaluation"
(should (equal (eshell-test-command-result "+ $(+ 1 2) 3") 6)))
@ -79,6 +98,36 @@
(eshell-command-result-p "echo ${echo hi}-${*echo there}"
"hi-there\n")))
(ert-deftest esh-var-test/quoted-interp-var ()
"Interpolate variable inside double-quotes"
(should (equal (eshell-test-command-result "echo \"$user-login-name\"")
user-login-name)))
(ert-deftest esh-var-test/quoted-interp-quoted-var ()
"Interpolate quoted variable inside double-quotes"
(should (equal (eshell-test-command-result
"echo \"hi, $'user-login-name'\"")
(concat "hi, " user-login-name)))
(should (equal (eshell-test-command-result
"echo \"hi, $\\\"user-login-name\\\"\"")
(concat "hi, " user-login-name))))
(ert-deftest esh-var-test/quoted-interp-lisp ()
"Interpolate Lisp form evaluation inside double-quotes"
(should (equal (eshell-test-command-result
"echo \"hi $(concat \\\"the\\\" \\\"re\\\")\"")
"hi there")))
(ert-deftest esh-var-test/quoted-interp-cmd ()
"Interpolate command result inside double-quotes"
(should (equal (eshell-test-command-result
"echo \"hi ${echo \\\"there\\\"}\"")
"hi there")))
(ert-deftest esh-var-test/quoted-interp-temp-cmd ()
"Interpolate command result redirected to temp file inside double-quotes"
(should (equal (eshell-test-command-result "cat \"$<echo hi>\"") "hi")))
;; Built-in variables