Über n Dateien iterieren?

8

Ich habe etwas ziemlich Einfaches, das ich tun möchte. Ich möchte in montageeinem Verzeichnis verwenden, das Tausende von Bildern enthält, mit sehr wenigen Optionen, nämlich:

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

... aber das ist einfach nicht gut genug, da es nur ungefähr 100 Bilder gleichzeitig erfasst; auch nicht

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

... wodurch alle Bilder gleichzeitig erfasst werden, da die resultierende Datei zu groß ist, um sie zu analysieren.

Was ich tun möchte, ist, ungefähr 100-200 Dateien gleichzeitig zu durchlaufen. Ich denke, dies könnte mit einer for-Schleife (?) Implementiert werden, aber ich bin nur ein bisschen verwirrt darüber, wie das geht. Ich denke, es gibt wahrscheinlich eine clevere Art zu verwenden find -execoder an xargsdie ich nicht denke. Ich benutze bash, aber ich benutze zshgelegentlich.

Abschließend suche ich nach einem Einzeiler, der bei 2600 Bilddateien die Montage etwa 13 oder 26 Mal (einmal für jeweils 100-200 Dateien) aufruft und bei n Dateien ein Vielfaches von n Mal aufgerufen werden kann .

ixtmixilix
quelle
1
Sind Ihre Dateien alle benannt DSC0100.JPG.svg... DSC2600.JPG.svg?
jw013

Antworten:

6

Eine bashMethode, die spezielle Array-Funktionen verwendet. wahrscheinlich zshmit einigen Modifikationen übersetzbar:

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
jw013
quelle
1
Ich fand, dass dieses Stück Bash-Scripting auch sehr erweiterbar ist. Ich habe es nur verwendet, um einige Dateien zu verschieben (16 Dateien pro Verzeichnis) und es hat beim ersten Versuch funktioniert, was eine kleine Überraschung war. Vielen Dank.
Ixtmixilix
5

Sie können dafür xargs verwenden; Leider ist es nicht möglich, -I (zum Eingeben in die Mitte einer Befehlszeile) und -L (zum Begrenzen der Anzahl von Dateien für einen einzelnen Aufruf der ausführbaren Datei) zu kombinieren. Daher habe ich diese Befehlszeile als Beispiel erstellt (aber Vorsicht vor Sonderzeichen in Dateinamen, sie werden nicht unterstützt):

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

Entfernen Sie das, echowenn Sie den Befehl wirklich ausführen möchten.

Vorsichtsmaßnahmen:

  • Dateinamen dürfen keine Leerzeichen oder andere Sonderzeichen enthalten
  • Die letzte Montagezeile enthält möglicherweise weniger als 100 Dateien

Aktualisieren:

Dies ist die entsprechende for-Schleife, die (wie ich hoffe) das Problem mit Leerzeichen in Dateinamen löst:

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

Update 2: Eine Python-Lösung, die gegen Sonderzeichen in Dateinamen immun sein sollte

#!/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] )
Daniel Kullmann
quelle
2
Wenn Sie empfehlen, lsin einer Pipe die Ausgabe zu analysieren, sollten Sie auch vor den vielen Gefahren warnen, die damit verbunden sind , und zu Beginn sicherstellen, dass die Leute sie sehen.
jw013
@ jw013 +1 Ja, das ist definitiv ein Problem. In seinem Beitrag gehe ich jedoch davon aus, dass er Fotos verwendet hat, die direkt von einer Digitalkamera importiert wurden und keine Sonderzeichen enthalten. Wie würden Sie vorschlagen, dieses Problem anzugehen?
Daniel Kullmann
Ja, es sieht so aus, als wären die Dateinamen relativ harmlos (daher keine Ablehnung). Das OP hat jedoch nicht wirklich angegeben, wie sie darüber hinaus aussehen *.svg(weshalb ich einen Kommentar zu der gestellten Frage gepostet habe). Im allgemeinsten Fall, in dem Sie alle Dateinamen verarbeiten müssen, müssen Sie auf Shell-Globbing und Arrays oder find -print0 | xargs -0Konstrukte zurückgreifen . Siehe meine Antwort für ein Beispiel des ersteren.
jw013
@ jw013 Deine Antwort ist wirklich nett! Ich habe mir nie die Mühe gemacht zu lernen, wie Arrays in Bash funktionieren. Vielleicht sollte ich.
Daniel Kullmann
2

Hier ist eine Version mit xargs, die für jeden Dateinamen sicher ist, jedoch eine temporäre Datei zum Speichern der Anzahl benötigt. Passen Sie '-n 100' an, um die Anzahl der Dateien pro Montage anzupassen. Sie können "printf" auch gegen "find -print0" austauschen, stellen Sie jedoch sicher, dass "count.temp" nicht gefunden wird.

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
Jander
quelle
2

Mit GNU Parallel können Sie Folgendes tun:

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

Es ist natürlich sicher für Dateien mit Sonderzeichen (wie Sie es normalerweise von GNU Parallel erwarten können).

Minimale Installation

Wenn Sie nur parallel benötigen und 'make' nicht installiert haben (möglicherweise ist das System alt oder 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/

Sehen Sie sich das Intro-Video für eine kurze Einführung an: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 oder unter http://tinyogg.com/watch/TORaR/ und http://tinyogg.com/watch/hfxKj /.

Ole Tange
quelle