Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm pretty noob when it comes to unix commands. What I want to do is to copy files within a lexicographic range.

Example of files:

-rw-r--r--   1 root root   15276 Oct  5  2004 a2ps.cfg
-rw-r--r--   1 root root    2562 Oct  5  2004 a2ps-site.cfg
-rw-r--r--   4 root root    4096 Feb  2  2007 acpi.txt
-rw-r--r--   1 root root      48 Feb  8  2008 adjtime.jpg
-rw-r--r--   4 root root    4096 Feb  2  2007 alchemist.jpg

Example of use:

  • Copy the 3 files from asps.cfg to acpi.txt
  • Copy the 3 files from a2ps-site.cfg to adjtime.jpg
  • Copy the 4 files from a2ps-site.cfg to alchemist.jpg

So is there any way to copy a range of files (from filename X to filename Y) into a directory?

Like: cp filenameX ... filenameY destination/

share|improve this question

migrated from stackoverflow.com Apr 16 '11 at 1:26

This question came from our site for professional and enthusiast programmers.

    
It's getting late here ... but it's not clear how or why you would copy 'cfg' files to 'jpg', and why you say there are 'ranges'. Also they appear to be owned by root. Will you do this copying as root? –  pavium Apr 15 '11 at 15:39
    
I guess he means copying all files from asps.cfg to acpi.txt to some destination folder, or that is at least how I interpreted it. –  Sami Lehtinen Apr 15 '11 at 15:43
    
The files are just exemples, I'm gonna copy lots of images. –  Tiax Apr 16 '11 at 0:12
1  
I think it would be more helpful if your example was your actual use case. As it stands, it is pretty difficult to determine what you need to do. –  Steven D Apr 16 '11 at 1:41
    
My actual use case is about 200 images so that would be to long.. I used 5 files case it is enough to show what Im after. What I need to do is the examples of use in the question. If you don't understand the question you can't answer it ;) –  Tiax Apr 16 '11 at 4:47

4 Answers 4

up vote 4 down vote accepted

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.

share|improve this answer
    
How do I define the function range()? I tried creating a file named range.sh with the funtion in it then running ./range.sh but then when I try using it, it says "-sh: range: command not found" –  Tiax Apr 16 '11 at 5:24
    
You need to source the file rather than run it so that the function is in your current bash environment. Like: source range.sh –  Steven D Apr 16 '11 at 6:03
    
If it's a function that you may use a lot, put it in your .bashrc. Otherwise you can just create a shell script on your path and leave out the function wrapping. –  camh Apr 16 '11 at 6:28

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
share|improve this answer

Simplest in this example is to use M-* to complete your pattern and remove the ones you don't want to copy.

% cp *[hit Escape and press *]
->
% cp file1 file2 file3 file4 ... 

and then just manually removing the files you don't want to copy. Anyways, this is a shell feature, not cp's.

You can refine the pattern as you wish. If you had many other files in the directory and you only want to copy ones starting with a, you'd just begin the pattern with that letter. You might want to lookup the shell's documentation on filename globbing. For example, http://tldp.org/LDP/abs/html/globbingref.html.

share|improve this answer
    
Since it's gonna be lots of images that way is to complicated.. –  Tiax Apr 16 '11 at 0:13

It seems file names are sorted and you seem to distribute in same amounts.

Get all file names to a single file

ls > all_files

Split the file into batches

split -l 4 all_files all_files-

You now have 4 filenames in each file, all_files-*

Pick one, say all_files-aa and do your copy:

xargs -I{} cp {} folder_for_batch_1 < all_files-aa

share|improve this answer
    
If the object is just to batch them, there is a MUCH easier way! ls | xargs -iX -n20 cp X target_folder/ would run cp on files in batches of 20 until they were all done. –  Caleb Apr 16 '11 at 14:36
    
The difference is, your version, target folder is same for all batches. –  Can Burak Cilingir Apr 17 '11 at 4:11

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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