The second method I propose bases on tweaking dired(+) such that copying of relative paths like
my_dir2 becomes possible.
The main problem with vanilla dired+ is already described in my other answer.
The following Elisp source code optionally replaces
dired-do-create-files. One can switch back to the original behavior by setting the customization option
dired-copy-relative-src to nil.
One also needs to give
dired-copy-file-recursive a non-nil
PARENT argument to accept the relative paths passed from
(defcustom dired-copy-relative-src t
"Use the relative source file name as target for copying."
(defun dired-do-create-files (op-symbol file-creator operation arg
&optional marker-char op1
"Create a new file for each marked file.
Prompt user for a target directory in which to create the new
files. The target may also be a non-directory file, if only
one file is marked. The initial suggestion for target is the
Dired buffer's current directory (or, if `dired-dwim-target' is
non-nil, the current directory of a neighboring Dired window).
OP-SYMBOL is the symbol for the operation. Function `dired-mark-pop-up'
will determine whether pop-ups are appropriate for this OP-SYMBOL.
FILE-CREATOR and OPERATION as in `dired-create-files'.
ARG as in `dired-get-marked-files'.
Optional arg MARKER-CHAR as in `dired-create-files'.
Optional arg OP1 is an alternate form for OPERATION if there is
only one file.
Optional arg HOW-TO determines how to treat the target.
If HOW-TO is nil, use `file-directory-p' to determine if the
target is a directory. If so, the marked file(s) are created
inside that directory. Otherwise, the target is a plain file;
an error is raised unless there is exactly one marked file.
If HOW-TO is t, target is always treated as a plain file.
Otherwise, HOW-TO should be a function of one argument, TARGET.
If its return value is nil, TARGET is regarded as a plain file.
If it return value is a list, TARGET is a generalized
directory (e.g. some sort of archive). The first element of
this list must be a function with at least four arguments:
operation - as OPERATION above.
rfn-list - list of the relative names for the marked files.
fn-list - list of the absolute names for the marked files.
target - the name of the target itself.
The rest of elements of the list returned by HOW-TO are optional
arguments for the function that is the first element of the list.
For any other return value, TARGET is treated as a directory."
(or op1 (setq op1 operation))
(let* ((fn-list (dired-get-marked-files nil arg))
(rfn-list (mapcar #'dired-make-relative fn-list))
(dired-one-file ; fluid variable inside dired-create-files
(and (consp fn-list) (null (cdr fn-list)) (car fn-list)))
(default (and dired-one-file
(not dired-dwim-target) ; Bug#25609
(expand-file-name (file-name-nondirectory (car fn-list))
(defaults (dired-dwim-target-defaults fn-list target-dir))
(target (expand-file-name ; fluid variable inside dired-create-files
(set (make-local-variable 'minibuffer-default-add-function) nil)
(setq minibuffer-default defaults))
(concat (if dired-one-file op1 operation) " %s to: ")
target-dir op-symbol arg rfn-list default))))
(into-dir (cond ((null how-to)
;; Allow users to change the letter case of
;; a directory on a case-insensitive
;; filesystem. If we don't test these
;; conditions up front, file-directory-p
;; below will return t on a case-insensitive
;; filesystem, and Emacs will try to move
;; foo -> foo/foo, which fails.
(if (and (file-name-case-insensitive-p (car fn-list))
(eq op-symbol 'move)
(expand-file-name (car fn-list)))
(file-name-nondirectory (car fn-list))
((eq how-to t) nil)
(t (funcall how-to target)))))
(if (and (consp into-dir) (functionp (car into-dir)))
(apply (car into-dir) operation rfn-list fn-list target (cdr into-dir))
(if (not (or dired-one-file into-dir))
(error "Marked %s: target must be a directory: %s" operation target))
;; rename-file bombs when moving directories unless we do this:
(or into-dir (setq target (directory-file-name target)))
file-creator operation fn-list
(if into-dir ; target is a directory
;; This function uses fluid variable target when called
;; inside dired-create-files:
(expand-file-name ,(if dired-copy-relative-src
`(file-relative-name from ,default-directory) ;; CHANGE: Use `file-relative-name' instead of `file-name-nondirectory'
(lambda (_from) target))
(defun dired-copy-file-recursive (from to ok-flag &optional
preserve-time top recursive)
(when (and (eq t (car (file-attributes from)))
(file-in-directory-p to from))
(error "Cannot copy `%s' into its subdirectory `%s'" from to))
(let ((attrs (file-attributes from)))
(if (and recursive
(eq t (car attrs))
(or (eq recursive 'always)
(yes-or-no-p (format "Recursive copies of %s? " from))))
(copy-directory from to preserve-time 'parents) ;; CHANGE: added 'parents
(or top (dired-handle-overwrite to))
(if (stringp (car attrs))
;; It is a symlink
(make-symbolic-link (car attrs) to ok-flag)
(copy-file from to ok-flag preserve-time))
(push (dired-make-relative from)
(dired-log "Can't set date on %s:\n%s\n" from err))))))
If you have installed the above Elisp code you can copy what you want as follows:
- Go to
- Copy all regular files by the following steps:
- Mark all by C-x h *m
- Unmark directories with C-u */
- Copy the marked files with C into
- Copy the directories without
my_dir2 as follows:
- Filter files in sub-directories by C-x d
- Mark all by C-x h *m
- Unmark unwanted directories by C-u %m
- Copy the marked stuff by C
GNU Emacs 26.2 (build 2, i686-pc-linux-gnu, GTK+ Version 3.22.30) of 2019-04-12
- started with
emacs -Q and M-x
dired+.Version: 21.2 but
dired-do-create-files are from vanilla dired