Итерировать по n файлам?

8

У меня есть кое-что довольно простое, что я хочу сделать. Я хочу использовать montageкаталог, содержащий тысячи изображений, с очень небольшим количеством опций, а именно:

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

... но это не достаточно хорошо, поскольку он захватывает только около 100 изображений одновременно; ни один не

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

... который захватывает все изображения одновременно, поскольку полученный файл слишком велик для анализа.

Что я хочу сделать, так это перебирать что-то вроде 100-200 файлов одновременно. Я предполагаю, что это может быть реализовано с помощью цикла for (?), Но я просто немного запутался, как это сделать. Я думаю, что есть, вероятно, умный способ использования find -execили xargsо котором я не думаю. Я использую bash, но я использую zshиногда.

Итак, в заключение, я ищу одну строку, которая, учитывая 2600 файлов изображений, вызывает монтаж около 13 или 26 раз (один раз для каждых 100-200 файлов), и учитывая n файлов, может быть вызвана кратно n раз ,

ixtmixilix
источник
1
Ваши файлы все названы DSC0100.JPG.svg... DSC2600.JPG.svg?
jw013

Ответы:

6

bashМетод, использующий специальный массив особенности; вероятно, можно перевести zshс некоторыми изменениями:

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
источник
1
я обнаружил, что этот сценарий bash также очень расширяем. я просто использовал его для перемещения некоторых файлов (16 файлов на каталог), и это сработало с первой попытки, что было немного удивительно. благодарю вас.
ixtmixilix
5

Вы можете использовать xargs для этого; к сожалению, невозможно объединить -I (для вставки в середину командной строки) и -L (для ограничения количества файлов для одного вызова исполняемого файла). Поэтому я создал эту командную строку в качестве примера (но остерегайтесь специальных символов в именах файлов, они не поддерживаются):

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

Удалите, echoесли вы хотите действительно выполнить команду.

Предостережения:

  • имена файлов не могут содержать пробелов или других специальных символов
  • последняя строка монтажа может содержать менее 100 файлов

Обновить:

Это соответствующий цикл for, который (я надеюсь) решает проблему с пробелами в именах файлов:

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

Обновление 2: решение Python, которое должно быть защищено от специальных символов в именах файлов

#!/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
Если вы собираетесь порекомендовать использовать lsв канале для анализа его выходных данных, вы также должны предупредить о многих опасностях делать это заметно, и в начале, чтобы убедиться, что люди видят это.
jw013
@ jw013 +1 Да, это действительно проблема. Однако его публикация позволила мне предположить, что он использовал фотографии, импортированные непосредственно с цифровой камеры, которые не содержат специальных символов. Как бы вы предложили решить эту проблему?
Даниэль Куллманн
Да, похоже, что имена файлов относительно мягкие (следовательно, нет отрицательного голоса). Однако ОП на самом деле не уточнил, как они выглядят за пределами *.svg(именно поэтому я разместил комментарий по поводу вопроса). В наиболее общем случае, когда вам нужно обработать все имена файлов, вам придется прибегнуть к глобализации оболочки и массивам или find -print0 | xargs -0конструкциям. Смотрите мой ответ для примера первого.
jw013
@ jw013 Ваш ответ действительно хорош! Я никогда не пытался узнать, как работают массивы в bash. Возможно я должен.
Даниэль Куллманн
2

Вот версия с использованием xargs, которая безопасна для любого имени файла, но требует временного файла для хранения счетчика. Настройте '-n 100', чтобы настроить количество файлов за монтаж. Вы также можете поменять местами «printf» с «find -print0», но убедитесь, что он не находит «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
Jander
источник
2

С GNU Parallel вы можете делать:

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

Это, конечно, безопасно для файлов со специальными символами (как обычно можно ожидать от GNU Parallel).

Минимальная установка

Если вам просто нужна параллель и у вас не установлена ​​программа 'make' (возможно, система старая или 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/

Посмотрите вступительное видео для быстрого ознакомления: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 или по адресу http://tinyogg.com/watch/TORaR/ и http://tinyogg.com/watch/hfxKj. /

Оле Танге
источник