¿Iterar sobre n archivos?

¿Iterar sobre n archivos?

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

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

...pero eso no es suficiente, ya que sólo captura unas 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 analizarlo.

Lo que yoquiero haceres iterar sobre entre 100 y 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 usarla find -execo xargsen la que no estoy pensando. Estoy usando bash, pero lo uso zshocasionalmente.

Entonces, en conclusión, estoy buscando una línea única que, dados 2600 archivos de imagen, llame al montaje aproximadamente 13 o 26 veces (una por cada 100-200 archivos), y dados n archivos, pueda llamarse múltiplo de n veces. .

Respuesta1

Un bashmétodo que utiliza características especiales de matriz; probablemente traducible 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

Respuesta2

Puedes usar xargs para eso; desafortunadamente, no es posible combinar -I (para insertar en medio de una línea de comando) y -L (para limitar la cantidad 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 archivos, 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 archivos no pueden contener espacios ni otros caracteres especiales.
  • la última línea de montaje puede tener menos de 100 archivos

Actualizar:

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

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 archivos

#!/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] )

Respuesta3

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 instalado 'make' (tal vez el sistema sea 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 vídeo de introducción para una introducción rápida: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1o en http://tinyogg.com/watch/TORaR/yhttp://tinyogg.com/watch/hfxKj/

Respuesta4

Aquí hay una versión que usa xargs que es segura para cualquier nombre de archivo, pero requiere un archivo temporal para almacenar el recuento. Ajuste '-n 100' para ajustar cuántos archivos por montaje. También puede intercambiar "printf" con "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

información relacionada