Subject: bug#54487: 29.0.50; connection-local value for `shell-file-name' not set in Dired buffers over Tramp

From: Michael Albinus <[email-masked]>
Date: Mon, 21 Mar 2022 11:25:28 +0100

Jim Porter <[email-masked]> writes:

Hi Jim,

> Hopefully I've summarized the issue correctly in the bug title. To see
> this in action, run the following from `emacs -Q' on an MS-Windows
> system ("host" in this example is a remote GNU/Linux system):
>
>   C-x C-f /ssh:host:~
>   M-x rgrep RET
>   some text RET RET RET
>
> The rgrep output will look something like:
>
>   find [...] --null -e "some text" "{}" +
>   find: paths must precede expression: `^^!^'

I confirm the bug, it happens also for me.

> You can click the "[...]" to see the full invocation. However, even
> without doing that, if you look carefully, you'll notice that the
> shell-quoting uses the MS-Windows rules, not that of /bin/sh. For the
> MS-Windows shell, spaces are quoted by wrapping the entire argument in
> double-quotes ("like this"); for /bin/sh, spaces are escaped via a
> backslash (like\ this).
>
> Presumably, that's because if you eval `shell-file-name' in the Dired
> buffer, it reports ".../path/to/cmdproxy.exe". When in a remote
> *file*, `shell-file-name' is correctly set to "/bin/sh".

It is not a problem of shell-file-name, if you check the Tramp debug
buffer you'll see, that a proper shell ("/bin/sh" in my case) is
applied.

The problem is rather quoting the arguments with shell-quote-argument. It
applies the quoting according to the value of system-type. If this is
'ms-dos or 'windows-nt, MS Windows quoting rules are applied.

The appended patch fixes this for me, could you pls check?

Best regards, Michael.

diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index ccc58e6773..85e872bfc2 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -611,6 +611,11 @@ grep-hello-file
       (write-region "Copyright\n" nil result))
     result))

+(defun grep-shell-quote-argument (argument)
+  (let ((system-type
+         (if (file-remote-p default-directory) 'not-windows system-type)))
+    (shell-quote-argument argument)))
+
 ;;;###autoload
 (defun grep-compute-defaults ()
   "Compute the defaults for the `grep' command.
@@ -636,8 +641,8 @@ grep-compute-defaults
 	  (intern (or (file-remote-p default-directory) "localhost")))
 	 (host-defaults (assq host-id grep-host-defaults-alist))
 	 (defaults (assq nil grep-host-defaults-alist))
-         (quot-braces (shell-quote-argument "{}"))
-         (quot-scolon (shell-quote-argument ";")))
+         (quot-braces (grep-shell-quote-argument "{}"))
+         (quot-scolon (grep-shell-quote-argument ";")))
     ;; There are different defaults on different hosts.  They must be
     ;; computed for every host once.
     (dolist (setting '(grep-command grep-template
@@ -820,7 +825,7 @@ grep-tag-default

 (defun grep-default-command ()
   "Compute the default grep command for \\[universal-argument] \\[grep] to offer."
-  (let ((tag-default (shell-quote-argument (grep-tag-default)))
+  (let ((tag-default (grep-shell-quote-argument (grep-tag-default)))
 	;; This a regexp to match single shell arguments.
 	;; Could someone please add comments explaining it?
 	(sh-arg-re
@@ -963,7 +968,7 @@ grep-expand-keywords
     ("<F>" . files)
     ("<N>" . (null-device))
     ("<X>" . excl)
-    ("<R>" . (shell-quote-argument (or regexp ""))))
+    ("<R>" . (grep-shell-quote-argument (or regexp ""))))
   "List of substitutions performed by `grep-expand-template'.
 If car of an element matches, the cdr is evalled in order to get the
 substitution string.
@@ -1134,10 +1139,10 @@ lgrep
 				    (mapconcat
                                      (lambda (ignore)
                                        (cond ((stringp ignore)
-                                              (shell-quote-argument ignore))
+                                              (grep-shell-quote-argument ignore))
                                              ((consp ignore)
                                               (and (funcall (car ignore) dir)
-                                                   (shell-quote-argument
+                                                   (grep-shell-quote-argument
                                                     (cdr ignore))))))
 				     grep-find-ignored-files
 				     " --exclude=")))
@@ -1245,44 +1250,44 @@ rgrep-default-command
   (grep-expand-template
    grep-find-template
    regexp
-   (concat (shell-quote-argument "(")
+   (concat (grep-shell-quote-argument "(")
            " " find-name-arg " "
            (mapconcat
-            #'shell-quote-argument
+            #'grep-shell-quote-argument
             (split-string files)
             (concat " -o " find-name-arg " "))
            " "
-           (shell-quote-argument ")"))
+           (grep-shell-quote-argument ")"))
    dir
    (concat
     (and grep-find-ignored-directories
          (concat "-type d "
-                 (shell-quote-argument "(")
-                 ;; we should use shell-quote-argument here
+                 (grep-shell-quote-argument "(")
+                 ;; we should use grep-shell-quote-argument here
                  " -path "
-                 (mapconcat (lambda (d) (shell-quote-argument (concat "*/" d)))
+                 (mapconcat (lambda (d) (grep-shell-quote-argument (concat "*/" d)))
                             (rgrep-find-ignored-directories dir)
                             " -o -path ")
                  " "
-                 (shell-quote-argument ")")
+                 (grep-shell-quote-argument ")")
                  " -prune -o "))
     (and grep-find-ignored-files
-         (concat (shell-quote-argument "!") " -type d "
-                 (shell-quote-argument "(")
-                 ;; we should use shell-quote-argument here
+         (concat (grep-shell-quote-argument "!") " -type d "
+                 (grep-shell-quote-argument "(")
+                 ;; we should use grep-shell-quote-argument here
                  " -name "
                  (mapconcat
                   (lambda (ignore)
                     (cond ((stringp ignore)
-                           (shell-quote-argument ignore))
+                           (grep-shell-quote-argument ignore))
                           ((consp ignore)
                            (and (funcall (car ignore) dir)
-                                (shell-quote-argument
+                                (grep-shell-quote-argument
                                  (cdr ignore))))))
                   grep-find-ignored-files
                   " -o -name ")
                  " "
-                 (shell-quote-argument ")")
+                 (grep-shell-quote-argument ")")
                  " -prune -o ")))))

 (defun grep-find-toggle-abbreviation ()