mv dir1/**/*.txt*(D.om[3,-1]) dir2/
Would move the regular files in
dir1 except for the 2 most recently modified ones to
**/: any level of sub-directory.
D: include hidden files and descend into hidden dirs.
.: only regular files (no symlink, directory...), equivalent for
om: sort by modification time (most recent first like with
[3,-1]: only from 3rd to last
(you can issue a
zmodload zsh/files to get a builtin
mv or use
zargs if you run into a arg list too big issue).
POSIXly, that simply can't be done without making some assumptions on the names of the files, the number of files and the length of their paths.
GNUly (with recent versions of GNU tools for
-z), you could do:
find dir1 -name '*.txt*' -type f -printf '%T@\t%p\0' |
sort -rnz | tail -zn +3 | cut -zf2- | xargs -r0 mv -t dir2
xargs have had
-0 options for decades, the addition of
tail is fairly recent. If you have older versions of those, you can always do:
find dir1 -type f -printf '%T@\t%p\0' | sort -rnz |
tr '\n\0' '\0\n' |
tail -n +3 | cut -f2- |
tr '\n\0' '\0\n' | xargs -r0 mv -t dir2
Note that while those solutions look for files recursively in
dir1 (including in subdirectories), they won't recreate the same directory structure in
dir2. That means that for instance if there were both a
dir1/subdir/file.txt, they would both end up being moved to
dir2/file.txt, one overwriting the other.