14

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?

12

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
  • 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
7

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).

  • 3
    … 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
5

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.

  • 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
1

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
  • @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. – jfg956 Dec 22 '12 at 11:11
0

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.

0

You can use a GUI file manager and resize the border of the window until every 5 files occupy a row, and then use mouse to select the first column...

-1

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.

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.