Move only the last 8 files in a directory to another directory

You can use for, which loops over the files in an ordered way, and allows us to avoid parsing the output of find or ls, to avoid issues with spaces and other special characters in filenames. Many thanks to @muru for improving this :)

i=0; j=$(stat ~/Documents/* --printf "%i\n" | wc -l); for k in ~/Documents/*; do if (( (j - ++i) < 8 )); then echo mv -v "$k" ~/AnotherDirectory; fi; done 

Test it first with echo, then remove echo to actually move the files.

As a script:

#!/bin/bash
i=0
j=$(stat ~/Documents/* --printf "%i\n" | wc -l )
for k in ~/Documents/*; do
  if (( (j - ++i) < 8 )); then
    echo mv -v -- "$k" ~/AnotherDirectory
  fi
done

again, remove echo after testing to move the files for real

Explanation

  • i=0 telling the shell to start iterating at 0
  • j=$(stat ~/Documents/* --printf "%i\n" | wc -l ) this is setting the variable j to an integer equal to the total number of files in the directory. Thanks to Serg's answer to my own question on how to count files reliably no matter what characters their names contain
  • do if (( (j - ++i) < 8 )) for each iteration of the loop, test whether the outcome of j minus the number of times the loop has been run is less than 8 and if it is then
  • mv -v -- "$k" ~/AnotherDirectory move the file to the new directory

You can do things like this using command substitution. In Bash:

mv $(ls -d [sort options] source/* | tail -n8) destination

The $(command) will run whatever is enclosed in it and substitute the text output into the outer command. That ls command will print the path to each file in the source directory sorted according the the flags you specify, one per line, so tail can just take the last few. Thus the above would expand to

mv source/file1 source/file2 source/file3 ... source/file8 destination

You can just run ls -d [sort options] source/* | tail -n8 to see what files it will copy.

Depending on how you're ordering the output of ls and the file naming, you may be able to do what you're looking for more simply just using some variant of mv source/name_* destination to copy everything starting with "name_" to the destination directory.

EDIT: The above breaks when there are spaces in the file names. A more complicated alternative that addresses this would be

ls -d1 [sort options] source/* | tail -n8 | tr '\n' '\0' | xargs --null mv -t destination

though it still makes use of parsing ls output to get sorting in any order that isn't alphabetical.


similar to t-mager's suggestion, you can use ls sort options and grab the last 8 files into a list and these are your files:

1 10 2 3 4 5 6 7 8 9

use the following command:
for n in `ls|tail -8`; do mv $n ~/temp; done