Seems easier with xargs
:
(cd dir1 && find . -name '*.ext' ! -type d -print0) |
(cd dir2 && xargs -r0 rm -f --)
For each foo.ext
and subdir/bar.ext
in dir1
would delete the same foo.ext
and subdir/bar.ext
in dir2
.
If you want to delete the files by the same name even if they're not found in the same directories in dir1
and dir2
(for instance in the example above, remove all of dir2/bar.ext
, dir2/any/level/of/subdirectory/foo.ext
) with zsh
:
typeset -U names
names=( dir1/**/*.ext(ND^/:t) )
rm -f -- dir2/**/(${(~j[|])names})(D^/)
typeset -U names
gives the unique attribute to the variable, so that when assigned as an array, the elements are unique (deduplicated).
**/
any level (including 0) of subdirectories
N
: N
ullglob: does not complain if there's no match. Actually, you migh as will leave it off for the shell to abort if doesn't find any .ext
file.
D
: D
otglob: doesn't skip hidden files.
^/
is like find
's ! -type d
to exclude the files of type directory (which can't be removed without first deleting their contents).
:t
: gets the t
ail, the base name
${(j[string])array}
joins the elements of the array with string
in-between. With ~
, the joining string is treated as a glob operator, so we end up with a (foo.ext|bar.ext|...)
glob.
Same golfed on one command using an anonymous function (and the u
parameter expansion flag in place of typeset -U
):
(){rm -f dir2/**/(${(~j[|])${(u)@}})(D^/)} dir1/**/*.ext(D^/:t)
Another approach that would be more efficient if there's a very large number of different names is to use an associative array:
typeset -A to_remove
: dir1/**/*.ext(ND^/e['to_remove[$REPLY:t]=1'])
rm -f -- dir2/**/*.ext(De['(( $to_remove[$REPLY:t] ))']^/)
If you just want to delete them at the root of dir2
(in the example above, remove dir2/foo.ext
and dir2/bar.ext
only, not those in subdirectories), remove the **/
in the rm
commands above or with GNU find
and sort
:
(cd dir1 && find . -name '*.ext' -printf '%f\0') |
LC_ALL=C sort -zu |
(cd dir2 && xargs -r0 rm -f --)