Iterar sobre n arquivos?


8

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

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

... mas isso não é bom 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 eu quero fazer é iterar sobre algo como 100-200 arquivos por vez. Eu 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 em xargsque não estou pensando. Estou usando bash, mas uso zshocasionalmente.

Então, em conclusão, estou procurando um liner que, com 2600 arquivos de imagem, chame a montagem cerca de 13 ou 26 vezes (uma vez para cada 100-200 arquivos) e com n arquivos, possa ser chamado várias vezes n .


1
Seus arquivos estão todos nomeados DSC0100.JPG.svg... DSC2600.JPG.svg?
Jw013

Respostas:


6

Um bashmétodo, usando recursos especiais de matriz; provavelmente traduzível para 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

1
Eu encontrei esse pouco de script bash também é muito extensível. Eu apenas o usei para mover alguns arquivos (16 arquivos por diretório) e funcionou na primeira tentativa, o que foi uma surpresa. obrigado.
Ixtmixilix

5

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 nos nomes dos 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:

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

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] )

2
Se você vai recomendar o uso lsem um tubo para analisar sua saída, também deve alertar sobre os muitos perigos de fazê-lo com destaque e, no início, para garantir que as pessoas o vejam.
Jw013

@ jw013 +1 Sim, isso é definitivamente uma preocupação. No entanto, sua postagem me permite assumir que ele estava usando fotos importadas diretamente de uma câmera digital, que não contém caracteres especiais. Como você sugeriria enfrentar esse problema?
Daniel kullmann

Sim, parece que os nomes dos arquivos são relativamente benignos (portanto, não há voto negativo). No entanto, o OP realmente não especificou como eles são *.svg(e foi por isso que eu postei um comentário sobre a pergunta). No caso mais geral, em que você precisa lidar com todos os nomes de arquivos, seria necessário recorrer a globbing de shell e matrizes ou find -print0 | xargs -0construções. Veja minha resposta para um exemplo do primeiro.
Jw013

@ jw013 Sua resposta é muito legal! Nunca me esforcei para aprender como as matrizes funcionam no bash. Talvez eu deva.
Daniel kullmann 13/07/12

2

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 o "printf" por um "find -print0", mas verifique se ele não encontra "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

2

Com o GNU Parallel, você pode:

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

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

Instalação mínima

Se você precisar apenas de um paralelo e não tiver o 'make' instalado (talvez o sistema seja antigo ou 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/

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.