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?


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

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


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.


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.


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

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


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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.