Tenho algo bastante simples que quero fazer. Quero usar montage
em 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 -exec
ou xargs
na qual não estou pensando. Estou usando bash
, mas uso zsh
ocasionalmente.
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 bash
método que usa recursos especiais de array; provavelmente traduzível zsh
com 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 echo
se 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