Iterate su n file?


8

Ho qualcosa di abbastanza semplice che voglio fare. Voglio usare montagesu una directory che contiene migliaia di immagini, con pochissime opzioni, vale a dire:

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

... ma questo non è abbastanza buono, in quanto cattura solo circa 100 immagini alla volta; nessuno dei due lo è

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

... che cattura tutte le immagini contemporaneamente, poiché il file risultante è troppo grande per essere analizzato.

Quello che voglio fare è iterare su qualcosa come 100-200 file alla volta. Immagino che questo possa essere implementato usando un ciclo for (?), Ma sono solo un po 'confuso su come farlo. Immagino che ci sia probabilmente un modo intelligente di usare find -execo xargsche non sto pensando. Sto usando bash, ma lo uso di zshtanto in tanto.

Quindi, in conclusione, sto cercando un liner che, dati 2600 file di immagini, chiama il montaggio circa 13 o 26 volte (una volta per ogni 100-200 file) e dato n file, può essere chiamato un multiplo di n volte .


1
I tuoi file sono tutti chiamati DSC0100.JPG.svg... DSC2600.JPG.svg?
jw013,

Risposte:


6

Un bashmetodo che utilizza speciali funzioni di array; probabilmente traducibile in zshcon qualche modifica:

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
ho trovato che questo po 'di script bash è anche molto estensibile. l'ho appena usato per spostare alcuni file (16 file per directory) e ha funzionato al primo tentativo, il che è stato un po 'una sorpresa. grazie.
ixtmixilix,

5

Puoi usare xargs per quello; sfortunatamente, non è probabilmente possibile combinare -I (per inserirsi nel mezzo di una riga di comando) e -L (per limitare il numero di file per una singola chiamata all'eseguibile). Pertanto, ho creato questa riga di comando come esempio (ma attenzione ai caratteri speciali nei nomi dei file, non sono supportati):

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

Rimuovere echose si desidera veramente eseguire il comando.

Avvertenze:

  • i nomi dei file non possono contenere spazi o altri caratteri speciali
  • l'ultima riga di montaggio potrebbe contenere meno di 100 file

Aggiornare:

Questo è il corrispondente ciclo per, che (spero) risolve il problema con spazi nei nomi dei file:

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

Aggiornamento 2: una soluzione Python, che dovrebbe essere immune da caratteri speciali nei nomi dei file

#!/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 hai intenzione di raccomandare di utilizzare lsin una pipe per analizzare il suo output, dovresti anche avvertire dei molti pericoli di farlo in modo prominente e all'inizio per assicurarti che le persone lo vedano.
jw013,

@ jw013 +1 Sì, questo è sicuramente un problema. Tuttavia, i suoi post lasciano supporre che stesse usando foto importate direttamente da una fotocamera digitale, che non contengono caratteri speciali. Come consiglieresti di affrontare questo problema?
Daniel Kullmann,

Sì, sembra che i nomi dei file siano relativamente benigni (quindi nessun downvote). Tuttavia, l'OP non ha ancora specificato come si presentano oltre *.svg(motivo per cui ho pubblicato un commento sulla domanda posta). Nel caso più generale in cui è necessario gestire tutti i nomi di file, è necessario ricorrere a shell globbing e array o find -print0 | xargs -0costrutti. Vedi la mia risposta per un esempio del primo.
jw013,

@ jw013 La tua risposta è davvero bella! Non mi sono mai sforzato di imparare come funzionano gli array in bash. Forse dovrei.
Daniel Kullmann,

2

Ecco una versione che usa xargs che è sicuro per qualsiasi nome di file, ma richiede un file temporaneo per memorizzare il conteggio. Regola '-n 100' per regolare il numero di file per montaggio. Puoi anche scambiare "printf" con "find -print0", ma assicurati che non trovi "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

Con GNU Parallel puoi fare:

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

Ovviamente è sicuro per file con caratteri speciali (come normalmente puoi aspettarti da GNU Parallel).

Installazione minima

Se hai solo bisogno di parallelo e non hai 'make' installato (forse il sistema è vecchio 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/

Guarda il video introduttivo per una rapida introduzione: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 o su http://tinyogg.com/watch/TORaR/ e http://tinyogg.com/watch/hfxKj /

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.