Iterar sobre n archivos?

8

Tengo algo bastante simple que quiero hacer. Quiero usar montageen un directorio que contenga miles de imágenes, con muy pocas opciones, a saber:

me@home$ montage -size 256x256 DSC01*.JPG.svg output.png

... pero eso no es lo suficientemente bueno, ya que solo toma alrededor de 100 imágenes a la vez; tampoco es

me@home$ montage -size 256x256 *.svg output.png

... que captura todas las imágenes al mismo tiempo, ya que el archivo resultante es demasiado grande para analizar.

Lo que quiero hacer es iterar sobre algo como 100-200 archivos a la vez. Supongo que esto podría implementarse usando un bucle for (?), Pero estoy un poco confundido acerca de cómo hacerlo. Supongo que probablemente haya una forma inteligente de usar find -execo xargsque no estoy pensando. Estoy usando bash, pero lo uso zshocasionalmente.

Entonces, en conclusión, estoy buscando un revestimiento que, dado 2600 archivos de imagen, llame al montaje unas 13 o 26 veces (una vez por cada 100-200 archivos), y dado n archivos, se puede llamar un múltiplo de n veces .

ixtmixilix
fuente
1
¿Todos sus archivos tienen nombre DSC0100.JPG.svg... DSC2600.JPG.svg?
jw013

Respuestas:

6

Un bashmétodo, que utiliza características especiales de la matriz; probablemente traducible a zshcon alguna modificación:

image_files=(*.svg) # use your own glob expression
n=200               # number of files per command line; adjust to taste
for ((i=0; i < ${#image_files[@]}; i+=n)); do
        montage -size 256x256 "${image_files[@]:i:n}" output-"$i".png
done
jw013
fuente
1
Encontré que este poco de scripting bash también es muy extensible. Simplemente lo usé para mover algunos archivos (16 archivos por directorio) y funcionó en el primer intento, lo que fue un poco sorprendente. gracias.
ixtmixilix
5

Puedes usar xargs para eso; desafortunadamente, no es posible combinar -I (para aislar en el medio de una línea de comando) y -L (para limitar el número de archivos para una sola llamada al ejecutable). Por lo tanto, creé esta línea de comando como ejemplo (pero tenga cuidado con los caracteres especiales en los nombres de archivo, no son compatibles):

 ls . | \
   xargs -n 100 echo | \
   (a=1; 
    while read args; do 
     echo montage -size 256x256 $args output-$a.png;
     a=$((a+1)); 
    done
   )

Elimine el echosi realmente desea ejecutar el comando.

Advertencias:

  • los nombres de archivo no pueden contener espacios u otros caracteres especiales
  • la última línea de montaje puede tener menos de 100 archivos

Actualizar:

Este es el bucle for correspondiente, que (espero) resuelve el problema con espacios en los nombres de archivo:

a=0
b=0
lst=
for f in *; do 
  a=$((a+1))
  lst="$lst '$f'"
  if test $a -ge 100; then 
    eval echo montage --args $lst target-$b.png
    b=$((b+1))
    a=0
    lst=
  fi 
done

Actualización 2: una solución de Python, que debería ser inmune a los caracteres especiales en los nombres de archivo

#!/usr/bin/env python
# iterate.py

"""Usage: 
%prog <number per call> <file pattern> <command prefix> -- <command postfix>
e.g.  %prog 100 "DSC01*.jpg.svg" montage -size 256x256 -- output-%i.png """

import sys,subprocess,glob,os

if len(sys.argv) < 5: 
  print __doc__.replace("%prog", os.path.basename(sys.argv[0]))
  sys.exit(1)

def chunks(l, n): 
  for i in xrange(0, len(l), n): yield l[i:i+n]

num, pattern, args = int(sys.argv[1]), sys.argv[2], sys.argv[3:]
files, idx = glob.glob(pattern), args.index("--")
before, after = args[0:idx], args[idx+1:]

for idx,chunk in enumerate(chunks(files,num)):
  subprocess.call( before + chunk + [s.replace("%i",str(idx)) for s in after] )
daniel kullmann
fuente
2
Si va a recomendar el uso lsen una tubería para analizar su salida, también debe advertir sobre los muchos peligros de hacerlo de manera prominente y al principio para asegurarse de que la gente lo vea.
jw013
@ jw013 +1 Sí, definitivamente es una preocupación. Sin embargo, su publicación me permitió suponer que estaba usando fotos directamente importadas de una cámara digital, que no contienen caracteres especiales. ¿Cómo sugeriría abordar ese problema?
daniel kullmann
Sí, parece que los nombres de los archivos son relativamente benignos (por lo tanto, no hay voto negativo). Sin embargo, el OP no ha especificado realmente cómo se ven más allá *.svg(por eso publiqué un comentario sobre la pregunta). En el caso más general en el que necesita manejar todos los nombres de archivo, tendría que recurrir al bloqueo de shell y matrices o find -print0 | xargs -0construcciones. Vea mi respuesta para un ejemplo de lo anterior.
jw013
@ jw013 ¡Tu respuesta es realmente buena! Nunca tomé el esfuerzo de aprender cómo funcionan las matrices en bash. Tal vez deberia.
daniel kullmann
2

Aquí hay una versión que usa xargs que es segura para cualquier nombre de archivo, pero requiere un archivo temporal para almacenar el conteo. Ajuste el '-n 100' para ajustar cuántos archivos por montaje. También puede intercambiar el "printf" con un "find -print0", pero asegúrese de que no encuentre "count.temp".

echo 1 >count.temp
printf "%s\0" *.svg | xargs -0 -n 100 sh -c '
    a=`cat count.temp`
    montage --blah "$@" output-"$a".png
    let a=a+1
    echo "$a" >count.temp
    '
rm count.temp
Jander
fuente
2

Con GNU Parallel puedes hacer:

parallel -N200 montage -size 256x256 {} output{#}.png ::: *.svg

Por supuesto, es seguro para archivos con caracteres especiales (como normalmente se puede esperar de GNU Parallel).

Instalación mínima

Si solo necesita paralelo y no tiene 'make' instalado (tal vez el sistema es antiguo o Microsoft Windows):

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
mv parallel sem dir-in-your-$PATH/bin/

Mire el video de introducción para una introducción rápida: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 o en http://tinyogg.com/watch/TORaR/ y http://tinyogg.com/watch/hfxKj / /

Ole Tange
fuente