11

Я хочу обработать много файлов, и, поскольку у меня здесь куча ядер, я хочу сделать это параллельно:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

Я знаю решение Makefile, но моим командам нужны аргументы из списка глобализации оболочки. Что я нашел это:

> function pwait() {
>     while [ $(jobs -p | wc -l) -ge $1 ]; do
>         sleep 1
>     done
> }
>

Чтобы использовать его, все, что нужно сделать, это поставить и после заданий и вызова pwait, параметр дает количество параллельных процессов:

> for i in *; do
>     do_something $i &
>     pwait 10
> done

Но это работает не очень хорошо, например, я попытался, например, с помощью цикла for, конвертирующего много файлов, но из-за которого я получил ошибку и оставил задания отмененными.

Я не могу поверить, что это еще не сделано, так как обсуждение списка рассылки zsh уже давно устарело. Так ты знаешь лучше?

4 ответа4

14

Makefile - хорошее решение вашей проблемы. Вы можете запрограммировать это параллельное выполнение в оболочке, но, как вы заметили, это сложно. Параллельная реализация make не только позаботится о запуске заданий и обнаружении их завершения, но также будет управлять балансировкой нагрузки, что сложно.

Требование к глобализации не является препятствием: существуют реализации make, которые его поддерживают. GNU make с расширением подстановочного знака, таким как $(wildcard *.c) и доступом к оболочке, таким как $(shell mycommand) (для получения дополнительной информации обратитесь к руководству по GNU make для функций). Это стандартная make для Linux, доступная в большинстве других систем. Вот скелет Makefile, который вы можете адаптировать к вашим потребностям:

sources = $(wildcard *.src)

all: $(sources:.src=.tgt)

%.tgt: $.src
    do_something $< $$(derived_params $<) >$@

Запустите что-то вроде make -j4 для параллельного выполнения четырех заданий или make -j -l3 для поддержания средней нагрузки около 3.

8

Я не уверен, на что похожи ваши аргументы. Но с помощью GNU Parallel http:// www.gnu.org/software/parallel/ вы можете сделать это для запуска одного задания на ядро процессора:

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

Если то, что вы хотите получить, это просто изменить расширение. {}} Может пригодиться:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

Посмотрите вступительное видео для GNU Parallel на http://www.youtube.com/watch?v=OpaiGYxkSuQ

6

Не подойдет ли вам команда wait оболочки?

for i in *
do
    do_something $i &
done
wait

Ваш цикл выполняет задание, затем ждет его, затем выполняет следующее задание. Если вышеперечисленное не работает для вас, то ваш может работать лучше, если вы переместите pwait после done .

3

Почему еще никто не упомянул xargs?

Предполагая, что у вас есть ровно три аргумента,

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

В противном случае используйте разделитель (для этого удобно использовать null):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

РЕДАКТИРОВАТЬ: для вышеупомянутого, каждый параметр должен быть разделен нулевым символом, а затем число параметров должно быть указано с помощью xargs -n.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .