From a8b71b3dc53883a2dcb3339bf4c7e1232c42d276 Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Sun, 2 Jul 2017 22:09:02 +0900 Subject: [PATCH] Extend dired-do-shell-command substitutions Substitute "`?`" inside command with the current file name. See details in: https://lists.gnu.org/archive/html/emacs-devel/2017-06/msg00618.html * lisp/dired-aux.el (dired-quark-subst-regexp, dired-star-subst-regexp): Mark as obsolete. (dired-isolated-string-re): New defun. (dired--star-or-qmark-p): New predicate. (dired-do-shell-command): Use dired--star-or-qmark-p. Substitute "`?`" with the current file name. * doc/emacs/dired.texi (Shell Commands in Dired): Update manual. ; * etc/NEWS: Mention it. --- doc/emacs/dired.texi | 26 ++++++++++++++++---------- etc/NEWS | 4 ++++ lisp/dired-aux.el | 43 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 22b0fcd4676..28cb51d88bb 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -875,25 +875,31 @@ treat it specially. @item Otherwise, if the command string contains @samp{?} surrounded by -whitespace, Emacs runs the shell command once @emph{for each file}, -substituting the current file name for @samp{?} each time. You can -use @samp{?} more than once in the command; the same file name -replaces each occurrence. +whitespace or @samp{`?`}, Emacs runs the shell command once +@emph{for each file}, substituting the current file name for @samp{?} +and @samp{`?`} each time. You can use both @samp{?} or @samp{`?`} more +than once in the command; the same file name replaces each occurrence. +If you mix them with @samp{*} the command signals an error. @item -If the command string contains neither @samp{*} nor @samp{?}, Emacs -runs the shell command once for each file, adding the file name at the +If the command string contains neither @samp{*} nor @samp{?} nor @samp{`?`}, +Emacs runs the shell command once for each file, adding the file name at the end. For example, @kbd{! uudecode @key{RET}} runs @code{uudecode} on each file. @end itemize - To iterate over the file names in a more complicated fashion, use an -explicit shell loop. For example, here is how to uuencode each file, -making the output file name by appending @samp{.uu} to the input file -name: + To iterate over the file names in a more complicated fashion, you might +prefer to use an explicit shell loop. For example, here is how to uuencode +each file, making the output file name by appending @samp{.uu} to the input +file name: @example for file in * ; do uuencode "$file" "$file" >"$file".uu; done +@end example + +The same example with @samp{`?`} notation: +@example +uuencode ? ? > `?`.uu @end example The @kbd{!} and @kbd{&} commands do not attempt to update the Dired diff --git a/etc/NEWS b/etc/NEWS index 0ba48f38aed..b9a492cb5ce 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -483,6 +483,10 @@ properties as intact as possible. * Changes in Specialized Modes and Packages in Emacs 26.1 +** Dired +You can now use '`?`' in 'dired-do-shell-command'; as ' ? ', it gets replaced +by the current file name. + *** html2text is now marked obsolete. *** smerge-refine-regions can refine regions in separate buffers diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index 121bebeb651..a57e5db17b7 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -51,6 +51,33 @@ into this list; they also should call `dired-log' to log the errors.") (defconst dired-star-subst-regexp "\\(^\\|[ \t]\\)\\*\\([ \t]\\|$\\)") (defconst dired-quark-subst-regexp "\\(^\\|[ \t]\\)\\?\\([ \t]\\|$\\)") +(make-obsolete-variable 'dired-star-subst-regexp nil "26.1") +(make-obsolete-variable 'dired-quark-subst-regexp nil "26.1") + +(defun dired-isolated-string-re (string) + "Return a regexp to match STRING isolated. +Isolated means that STRING is surrounded by spaces or at the beginning/end +of a string followed/prefixed with an space. +The regexp capture the preceding blank, STRING and the following blank as +the groups 1, 2 and 3 respectively." + (format "\\(\\`\\|[ \t]\\)\\(%s\\)\\([ \t]\\|\\'\\)" string)) + +(defun dired--star-or-qmark-p (string match &optional keep) + "Return non-nil if STRING contains isolated MATCH or `\\=`?\\=`'. +MATCH should be the strings \"?\", `\\=`?\\=`', \"*\" or nil. The latter +means STRING contains either \"?\" or `\\=`?\\=`' or \"*\". +If optional arg KEEP is non-nil, then preserve the match data. Otherwise, +this function changes it and saves MATCH as the second match group. + +Isolated means that MATCH is surrounded by spaces or at the beginning/end +of STRING followed/prefixed with an space. A match to `\\=`?\\=`', +isolated or not, is also valid." + (let ((regexps (list (dired-isolated-string-re (if match (regexp-quote match) "[*?]"))))) + (when (or (null match) (equal match "?")) + (setq regexps (append (list "\\(\\)\\(`\\?`\\)\\(\\)") regexps))) + (cl-some (lambda (x) + (funcall (if keep #'string-match-p #'string-match) x string)) + regexps))) ;;;###autoload (defun dired-diff (file &optional switches) @@ -658,13 +685,13 @@ If there is a `*' in COMMAND, surrounded by whitespace, this runs COMMAND just once with the entire file list substituted there. If there is no `*', but there is a `?' in COMMAND, surrounded by -whitespace, this runs COMMAND on each file individually with the -file name substituted for `?'. +whitespace, or a `\\=`?\\=`' this runs COMMAND on each file +individually with the file name substituted for `?' or `\\=`?\\=`'. Otherwise, this runs COMMAND on each file individually with the file name added at the end of COMMAND (separated by a space). -`*' and `?' when not surrounded by whitespace have no special +`*' and `?' when not surrounded by whitespace nor `\\=`' have no special significance for `dired-do-shell-command', and are passed through normally to the shell, but you must confirm first. @@ -704,8 +731,8 @@ can be produced by `dired-get-marked-files', for example." (dired-read-shell-command "! on %s: " current-prefix-arg files) current-prefix-arg files))) - (let* ((on-each (not (string-match-p dired-star-subst-regexp command))) - (no-subst (not (string-match-p dired-quark-subst-regexp command))) + (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep))) + (no-subst (not (dired--star-or-qmark-p command "?" 'keep))) (star (string-match-p "\\*" command)) (qmark (string-match-p "\\?" command)) ;; Get confirmation for wildcards that may have been meant @@ -768,12 +795,10 @@ can be produced by `dired-get-marked-files', for example." ";" "&")) (stuff-it - (if (or (string-match-p dired-star-subst-regexp command) - (string-match-p dired-quark-subst-regexp command)) + (if (dired--star-or-qmark-p command nil 'keep) (lambda (x) (let ((retval (concat cmd-prefix command))) - (while (string-match - "\\(^\\|[ \t]\\)\\([*?]\\)\\([ \t]\\|$\\)" retval) + (while (dired--star-or-qmark-p retval nil) (setq retval (replace-match x t t retval 2))) retval)) (lambda (x) (concat cmd-prefix command dired-mark-separator x))))) -- 2.39.2