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 have a lot of files in a folder, named like 00802_Bla_Aquarium_XXXXX.jpg. Now I need to copy every 4th file to a subfolder, saying in selected/.

00802_Bla_Aquarium_00020.jpg <= this one
00802_Bla_Aquarium_00021.jpg
00802_Bla_Aquarium_00022.jpg
00802_Bla_Aquarium_00023.jpg
00802_Bla_Aquarium_00024.jpg <= this one
00802_Bla_Aquarium_00025.jpg
00802_Bla_Aquarium_00026.jpg
00802_Bla_Aquarium_00027.jpg
00802_Bla_Aquarium_00028.jpg <= this one
00802_Bla_Aquarium_00029.jpg

How do I do this?

share|improve this question
    
You might be able to do like the solutions to superuser.com/q/396536/87552 on some output of ls. See also stackoverflow.com/q/4553751/789593 which asks about every second line. –  N.N. Dec 21 '12 at 10:33
    
If they're numbered and you know the last number, you might consider a variation of for n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done –  Ulrich Schwarz Dec 21 '12 at 11:25

6 Answers 6

up vote 6 down vote accepted

With zsh, you could do:

n=0; cp 00802_Bla_Aquarium_?????.jpg(^e:'((n++%4))':) /some/place

POSIXly, same idea, just a bit more verbose:

# put the file list in the positional parameters ($1, $2...).
# the files are sorted in alphanumeric order by the shell globbing
set -- 00802_Bla_Aquarium_?????.jpg

n=0
# loop through the files, increasing a counter at each iteration.
for i do
  # every 4th iteration, append the current file to the end of the list
  [ "$(($n % 4))" -eq 0 ] && set -- "$@" "$i"

  # and pop the current file from the head of the list
  shift
  n=$(($n + 1))
done

# now "$@" contains the files that have been appended.
cp -- "$@" /some/place

Since those filenames don't contain any blank or wildcard characters, you could also do:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
share|improve this answer
    
Can you please put comment also in posix code –  Rahul Patil Dec 21 '12 at 10:47
    
@ChrisDown, for i do is standard and actually more portable than for i; do and is the canonical way to loop over the positional parameters. –  Stéphane Chazelas Dec 21 '12 at 17:34

Simply with bash, you can do:

n=0
for file in ./*.jpg; do
   test $n -eq 0 && cp "$file" selected/
   n=$((n+1))
   n=$((n%4))
done

The pattern ./*.jpg will be replaced by an alphabetically sorted list of file names as stated by the bash man, so it should fit your purpose.

share|improve this answer
    
In bash, your three line inside the for loop can be replaced with just this line: (((++n)%4)) || cp "$file" selected/. ;-). The problem though is that you're forking on a cp for each file which is not really efficient. –  gniourf_gniourf Dec 21 '12 at 17:33

In bash, a funny possibility, that will work rather well here:

cp 00802_Bla_Aquarium_*{00..99..4}.jpg selected

That's definitely the shortest and most efficient answer: no subshell, no loop, no pipe, no awkward external process; just one fork to cp (that you can't avoid anyway) and one bash brace expansion and glob (that you can get rid of altogether since you know how many files you have).

share|improve this answer
2  
… and 2 assumptions: files are numbered continuously and the first one is divisible with 4. –  manatwork Dec 21 '12 at 17:23
    
@manatwork no problem if first one is not divisible by 4: e.g., cp 00802_Bla_Aquarium_*{03..99..4}.jpg selected if you want 3 mod 4. (I'm using the wonderful fact that 100 is divisible by 4). Now, about continuously numbered files, all the other answers assume the same. –  gniourf_gniourf Dec 21 '12 at 17:26
    
Which other answers assume continuous numbering? I found only Ulrich Schwarz's comment and no such answer. –  manatwork Dec 21 '12 at 17:38

If you know you will not have newlines in your file names, you could use:

find . -maxdepth 1 -name "*.jpg" | sort | while IFS= read -r file; do
  cp "$file" selected/
  IFS= read -r; IFS= read -r; IFS= read -r
done
share|improve this answer
    
@Gilles: thanks for the edit. I removed the file variable in the 3 read though, as I want to point out that they are not needed. –  jfgagne Dec 22 '12 at 11:11

If you have ruby installed you can use the following one-liner. Note that it assumes that the directory selected exists.

ruby -rfileutils -e 'files = Dir.glob("*.jpg").each_slice(4) { |file| FileUtils.cp(file.first, "selected/" + file.first) }'

It takes a list of all files with the extension .jpg in the current directory and slices it into lists of four elements and copies the first element from each such list to the directory selected in the current directory.

share|improve this answer

Looking at stackoverflow.com/q/4553751/789593 which offers a solution to copy every second one as @N.N. mentioned, a very simple solution would be:

  • Remove the first file from the starting folder
  • Copy every second one from the starting folder to an intermediate folder
  • Copy every seconde one from the intermediate folder into the final folder
  • Add the first file manually

It may not be very efficient as it requires an extra copy step bit it should be very easy to do.

share|improve this answer

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.