Iterar em n arquivos?

Iterar em n arquivos?

Tenho algo bastante simples que quero fazer. Quero usar montageem um diretório que contém milhares de imagens, com pouquíssimas opções, a saber:

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

...mas isso não é suficiente, pois captura apenas cerca de 100 imagens por vez; Nem é

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

...que captura todas as imagens ao mesmo tempo, pois o arquivo resultante é muito grande para ser analisado.

O que euquerer fazeré iterar algo como 100-200 arquivos por vez. Acho que isso poderia ser implementado usando um loop for (?), mas estou um pouco confuso sobre como fazer isso. Acho que provavelmente existe uma maneira inteligente de usar find -execou xargsna qual não estou pensando. Estou usando bash, mas uso zshocasionalmente.

Então, para concluir, estou procurando um liner que, dados 2.600 arquivos de imagem, chame a montagem cerca de 13 ou 26 vezes (uma vez para cada 100-200 arquivos) e, dados n arquivos, possa ser chamado de múltiplo de n vezes .

Responder1

Um bashmétodo que usa recursos especiais de array; provavelmente traduzível zshcom alguma modificação:

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

Responder2

Você pode usar xargs para isso; infelizmente, não é possível combinar -I (para inserir no meio de uma linha de comando) e -L (para limitar o número de arquivos para uma única chamada ao executável). Portanto, criei esta linha de comando como exemplo (mas cuidado com caracteres especiais em nomes de arquivos, eles não são suportados):

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

Remova o echose você realmente deseja executar o comando.

Ressalvas:

  • os nomes dos arquivos não podem conter espaços ou outros caracteres especiais
  • a última linha de montagem pode conter menos de 100 arquivos

Atualizar:

Este é o loop for correspondente, que (espero) resolve o problema com espaços nos nomes dos arquivos:

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

Atualização 2:Uma solução python, que deve ser imune a caracteres especiais em nomes de arquivos

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

Responder3

Com o GNU Parallel você pode fazer:

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

É claro que é seguro para arquivos com caracteres especiais (como normalmente você pode esperar do GNU Parallel).

Instalação mínima

Se você só precisa de paralelo e não tem o 'make' instalado (talvez o sistema seja antigo ou 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/

Assista ao vídeo de introdução para uma introdução rápida: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1ou em http://tinyogg.com/watch/TORaR/ehttp://tinyogg.com/watch/hfxKj/

Responder4

Aqui está uma versão usando xargs que é segura para qualquer nome de arquivo, mas requer um arquivo temporário para armazenar a contagem. Ajuste o '-n 100' para ajustar quantos arquivos por montagem. Você também pode trocar "printf" por "find -print0", mas certifique-se de que não encontre "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

informação relacionada