Copy multiple files from filename X to filename Y?

A generic approach is to define a shell function that prints a range of filenames. A simple version of this function will list all the files between two filenames inclusive in the current directory:

range()
{
    ls | sed -n "/^$1\$/,/^$2\$/p"
}

This uses a sed range saying to print out anything between the two given strings ($1 and $2).

You can then use this function in your cp(1) command:

cp $(range asps.cfg acpi.txt) /destination/path

This will fail if you have filenames with spaces in them. For that, you need to get a little more complicated:

range asps.cfg acpi.txt | xargs -d '\n' -I {} cp {} /destination/path

That will fail if your filenames have newlines (\n) in them. That is very unlikely, so fixing that is left as an exercise for the reader.

You could roll that together into another shell function:

cprange()
{
    [ $# -eq 3 ] || { echo "usage: cprange start end dir" >&2; return 1;}
    range "$1" "$2" | xargs -d '\n' -I {} cp {} "$3"
}

and use it like:

cprange filenameX filenameY destination

Remember, range only works for filenames in the current directory. You can expand on that in a number of ways (additional directory parameter to range, extract a path from arguments to range using dirname, etc) but I'll leave that up to you based on your requirements. Please ask if you want more details here.


Zsh has an easy way to select a range of files out of the result of a glob (a glob is a wildcard match). On the plus side, the command is very short to write. On the minus site, you'll have to figure out the ordinals of the files you want to write. For example, the following command copies the 21st, 22nd, …, 40th file with a .jpg extension in the current directory, with the files enumerated in alphabetical order:

cp *.jpg([21,40]) destination/

Zsh has an option that may or may not be helpful to you: under setopt numeric_glob_sort, foo9bar.jpg will be sorted before foo10bar.jpg. You can also choose a different sorting order in the glob qualifiers, e.g. cp *.jpg(om[21,40]) destination/ to pick the 21st to 40th most recent files (use a capital O to sort in the opposite order). See glob qualifiers in the manual for more information.

Under any shell, you could write a loop that iterates over the files, starts copying at the first file you want to write, and stops after the last file. Warning, untested.

in_range=
for x in *.jpg; do
  if [ "$x" = "first-file.jpg" ]; then in_range=1; fi
  if [ -n "$in_range" ]; then cp "$x" destination/; fi
  if [ "$x" = "last-file.jpg" ]; then break; fi
done

Tags:

Cp