Cómo copiar cada 4to archivo en una carpeta

15

Tengo muchos archivos en una carpeta, nombrados como 00802_Bla_Aquarium_XXXXX.jpg. Ahora necesito copiar cada 4to archivo a una subcarpeta, diciendo en 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

¿Cómo hago esto?

Aebersold
fuente
Es posible que pueda hacer como las soluciones a superuser.com/q/396536/87552 en alguna salida de ls. Consulte también stackoverflow.com/q/4553751/789593 que pregunta sobre cada segunda línea.
NN
Si están numerados y conoce el último número, podría considerar una variación defor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz

Respuestas:

12

Con zsh, podrías hacer:

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

POSIXY, la misma idea, solo un poco más detallado:

# 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

Dado que esos nombres de archivo no contienen caracteres en blanco o comodín, también puede hacer:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Stéphane Chazelas
fuente
¿Puedes poner un comentario también en el código posix?
Rahul Patil
@ChrisDown, for i does estándar y en realidad es más portátil que for i; doy es la forma canónica de recorrer los parámetros posicionales.
Stéphane Chazelas
7

En bash, una posibilidad divertida, que funcionará bastante bien aquí:

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

Esa es definitivamente la respuesta más corta y eficiente: sin subshell, sin bucle, sin tubería, sin awkproceso externo de barrio; solo una bifurcación para cp(que no puede evitar de todos modos) y una expansión de llave bash y glob (que puede eliminar por completo ya que sabe cuántos archivos tiene).

gniourf_gniourf
fuente
44
... y 2 supuestos: los archivos están numerados continuamente y el primero es divisible con 4.
manatwork
@manatwork no hay problema si el primero no es divisible por 4: por ejemplo, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedsi lo desea 3 mod 4. (Estoy usando el hecho maravilloso de que 100 es divisible por 4). Ahora, sobre archivos numerados continuamente, todas las otras respuestas asumen lo mismo.
gniourf_gniourf
¿Qué otras respuestas suponen una numeración continua? Solo encontré el comentario de Ulrich Schwarz y ninguna respuesta.
manatwork
5

Simplemente con bash, puedes hacer:

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

El patrón ./*.jpgserá reemplazado por una lista ordenada alfabéticamente de nombres de archivos según lo indicado por el hombre de bash, por lo que debe ajustarse a su propósito.

jfg956
fuente
En bash, su línea de tres en el interior del forbucle se puede reemplazar con sólo esta línea: (((++n)%4)) || cp "$file" selected/. ;-). Sin embargo, el problema es que está bifurcando un archivo cppara cada archivo que no es realmente eficiente.
gniourf_gniourf
1

Si lo ha rubyinstalado, puede usar el siguiente one-liner. Tenga en cuenta que supone que el directorio seleccionado existe.

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

Toma una lista de todos los archivos con la extensión .jpg en el directorio actual y la divide en listas de cuatro elementos y copia el primer elemento de cada lista en el directorio seleccionado en el directorio actual.

NN
fuente
1

Si sabe que no tendrá nuevas líneas en sus nombres de archivo, puede usar:

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
jfg956
fuente
@Gilles: gracias por la edición. Sin embargo, eliminé la variable del archivo en la lectura 3, ya que quiero señalar que no son necesarios.
jfg956
1

Puede usar un administrador de archivos GUI y cambiar el tamaño del borde de la ventana hasta que cada 5 archivos ocupen una fila, y luego usar el mouse para seleccionar la primera columna ...

John Chain
fuente
-1

Mirando stackoverflow.com/q/4553751/789593 que ofrece una solución para copiar cada segundo como se menciona en @NN, una solución muy simple sería:

  • Eliminar el primer archivo de la carpeta de inicio
  • Copie cada segundo de la carpeta de inicio a una carpeta intermedia
  • Copie cada segundo de la carpeta intermedia en la carpeta final
  • Agregue el primer archivo manualmente

Puede que no sea muy eficiente ya que requiere un bit de paso de copia adicional, debería ser muy fácil de hacer.

Dennis Jaheruddin
fuente