You can do this with GNU find
and GNU mv
:
find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +
Basically, the way that works if that find
goes through the entire directory tree and for each file (-type f
) that is not in the top-level directory (-mindepth 2
), it runs a mv
to move it to the directory you want (-exec mv … +
). The -t
argument to mv
lets you specify the destination directory first, which is needed because the +
form of -exec
puts all the source locations at the end of the command. The -i
makes mv
ask before overwriting any duplicates; you can substitute -f
to overwrite them without asking (or -n
to not ask or overwrite).
As Stephane Chazelas points out, the above only works with GNU tools (which are standard on Linux, but not most other systems). The following is somewhat slower (because it invokes mv
multiple times) but much more universal:
find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'
POSIXly, passing more than one argument, but using sh
to reorder the list of arguments for mv
so the target directory comes last:
LC_ALL=C find /dir1 -path '/dir1/*/*' -type f -exec sh -c '
exec mv "$@" /dir1' sh {} +
find . -type f -print0 | xargs -0 -I file mv --backup=numbered file .
I also follow with another commandrename 's/((?:\..+)?)\.~(\d+)~$/_$2$1/' *.~*~
to keep the numbering of duplicated files before the extension.