How can I move everything but the last n files, from dir1 to dir2?.

I currently do this, setting the time as an approximate for n, in my case n=2 each 10 minutes.

find /dir1/ -name '*.txt*' -mmin +10 -type f -exec mv "{}" /dir2/ \;

A similar command, could work, but i am not certain, could somebody confirm how should adapt this?

ls -1tr | head -n -2 | xargs -d '\n' mv -f --
  • 3
    And I don't think a new answer is needed to replace rm by mv or changing on how many time-sorted files the action should be performed. (-:
    – Philippos
    Jul 13 '17 at 15:09
  • What do you mean by filled? Maybe you should use -size instead of -mmin ?
    – mrc02_kr
    Jul 13 '17 at 15:12
  • Do you mean that files such as 20170713111152.txt and 20170713111152.txt_aux are created and appended to for 10 minutes until new ones, such as 20170713112152.txt are created?
    – Jeff Schaller
    Jul 13 '17 at 15:13
  • Take a solution, try to adapt it to your needs and come back with it if it doesn't work.
    – Philippos
    Jul 15 '17 at 4:49

With zsh:

mv dir1/**/*.txt*(D.om[3,-1]) dir2/

Would move the regular files in dir1 except for the 2 most recently modified ones to dir2.

  • **/: any level of sub-directory.
  • D: include hidden files and descend into hidden dirs.
  • .: only regular files (no symlink, directory...), equivalent for find's -type f.
  • om: sort by modification time (most recent first like with ls -t).
  • [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

While GNU sort and xargs have had -z/-0 options for decades, the addition of -z for cut and 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/file.txt and dir1/subdir/file.txt, they would both end up being moved to dir2/file.txt, one overwriting the other.


Since the datestamped files will be gathered together in order with shell globs (e.g. *.txt), you could use arrays (such as in bash):

n=2 ## how many of each you want to keep
echo mv "${txtfiles[@]:0:movetxt}" /dir/aux2/
echo mv "${auxfiles[@]:0:moveaux}" /dir/aux2/

Remove the last two echo pieces when it looks like it's doing the right thing.


An easier way of doing this:

mv `ls -tr sourcedir | head -n -2` destdir 

Note: This assumes you know there are more than two files in the directory. If you're not sure, you can count them with ls | wc -l

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.