Используйте parallel для разделения множества каталогов на подкаталоги или распараллеливания этой задачи

Используйте parallel для разделения множества каталогов на подкаталоги или распараллеливания этой задачи

Я хочу распараллелить разбиение множества каталогов на подкаталоги с помощью parallelили с использованием другого инструмента или метода.

Например, у меня есть 1 000 000 каталогов с контентом, но это слишком много для одного каталога, поэтому я хочу создать 10 каталогов в главном каталоге и переместить в каждый из них 100 000 исходных каталогов. Я также хочу использовать сортировку по дате. Я уже спрашиваланалогичный вопрос здесь, но это не дубликат, потому что я попробовал новые команды, получил новые результаты и теперь переформулировал вопрос.

Итак, я уже попробовал это.

ls -tr|parallel -n100000 mkdir "dir_{#}"\;mv {} "dir_{#}" 

и это

ls -tr | parallel -j10 -n100000 --no-notice -k 'mkdir -p dir_{#}; mv {} -t dir_{#}' 

команды, но он перемещает только ~10 000 в один подкаталог (иногда ~6200, иногда ~12 500) и создает слишком много подкаталогов - иногда в 10 раз больше, чем мне нужно.

Я также пытался использовать это:

ls -dtr * | parallel -j10 -n100000 --no-notice -k 'mkdir -p dir_{#}; mv {} -t dir_{#}'

но это дало bash: /bin/ls: Argument list too long.

Конечно, мне не нужно ровно 100 000 каталогов в каждом подкаталоге, это может быть 101 000 или 98 500 каталогов, это должно быть число в диапазоне 100 000.

Как я могу выполнить эту задачу параллельно или с помощью parallel?

решение1

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

getconf ARG_MAX

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

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

ls -tr | parallel -N 100000 --pipe -k "mkdir dir_{#}; parallel -X mv -t dir_{#}"

Объяснение

  • На первом этапе используется возможность --pipeразбить stdin на определенное количествоменьшие стандартные вводы, каждый из которых содержит n строк, как указано в параметре -N. Вы можете наблюдать эффект, используя этот пример

    seq 1000000 | parallel -N 100000 --pipe wc -l
    

    что дает точное разделение на отметке 100000

    100000
    100000
    100000
    ...
    
  • На втором этапе внутренние параллели принимаютменьшие стандартные вводыв качестве нового стандартного ввода для выполнения заданий -Xопция вставляет столько аргументов, сколько позволяет длина командной строки

    mkdir dir_{#}; parallel -X mv -t dir_{#}
    

решение2

Эта проблема касается тяжелого ввода-вывода. Сомневаюсь, что это parallelдействительно полезно в данной ситуации.

В любом случае я предлагаю вам рассмотреть «традиционный» подход:

 mkdir dir_{1..10}
 ls -tr | nl | \
    awk '$2 !~ /^dir_/ {i=1+int($1/100000); print $2 | "xargs mv -t dir_"i}'

где

  • ls -tr | nl сортирует каталоги по дате и добавляет вспомогательный номер каталога
  • $2 !~ /^dir_/используется для пропуска только что созданных папок.
  • i=1+int($1/100000)вычисляет номер папки на основе dir-number
  • print $2 | "xargs mv -t dir_"iдвижется без распространения процесса

Если возможно, сравните также соответствующее время: time ....(и поделитесь с нами результатами ☺)

Связанный контент