Выполнение команды над слишком большим количеством файлов - PullRequest
1 голос
/ 20 февраля 2020

Я хочу зашифровать и расшифровать большие файлы (примерно 20 миллионов строк) текста. Служба шифрования, которую я использую, может шифровать только максимальный размер 64 КБ. Для целей этого вопроса предположим, что мы застряли с этим сервисом.

Мое решение состоит в том, чтобы разбить огромный файл на куски по 64 КБ, зашифровать их все параллельно и поместить зашифрованные части в tar.gz. Каждая часть пронумерована как part-xxx, чтобы убедиться, что я могу восстановить исходный файл. Во время расшифровки я распаковываю, расшифровываю каждую часть параллельно и сопоставляю результаты по порядку.

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

  1. Сеансы tmux умирают, и я выхожу из системы. Нет журналов, нет ничего.

  2. Я получаю это:

/home/estergiadis/kms/decrypt.sh: line 45: /usr/bin/find: Argument list too long
/home/estergiadis/kms/decrypt.sh: line 46: /bin/rm: Argument list too long

Я попробовал несколько решений, основанных на xargs, но безуспешно. Вот интересный код:

echo "Decrypting chunks in parallel."
# -1 -f in ls helped me go from scenario 1 to scenario 2 above. 
# Makes sense since I don't need sorting at this stage.
ls -1 -f part-* | xargs -I % -P 32 bash -c "gcloud kms decrypt --ciphertext-file % --plaintext-file ${OUTPUT}.%"

# Best case scenario, we die here
find $OUTPUT.part-* | xargs cat > $OUTPUT
rm $OUTPUT.part-*

Еще интереснее: когда find и rm сообщают о проблеме, я могу go во временную папку со всеми частями, выполнить те же самые команды сам и все работает.

В случае, если это имеет значение, все это происходит в файловой системе, смонтированной в ОЗУ. Однако проблема с ОЗУ не может быть: я на машине с 256 ГБ ОЗУ, файлы занимают 1-2 ГБ, а htop никогда не показывает более 10% использования.

1 Ответ

2 голосов
/ 21 февраля 2020

Ваша проблема с этими:

ls -1 -f part-* | ...
find $OUTPUT.part-* | ...
rm $OUTPUT.part-*

Если у вас слишком много частей (part-*, et c), расширение имени файла, выполненное оболочкой, приведет к команде со слишком большим количеством аргументы или вы можете превысить максимальную длину команды.

find + xargs позволяет вам преодолеть это. Вы можете заменить любую команду, которая использует глобус, для вывода списка файлов в текущем каталоге, например:

find . -name GLOB -print -o ! -path . -prune | xargs CMD

-o ! -path . -prune указывает find не спускаться в подкаталоги. xargs гарантирует, что сгенерированные командные строки не превышают максимальный аргумент или пределы строки.

Таким образом, для трех строк вы можете сделать:

globwrap(){
    glob="$1"
    shift

    find . -name "$glob" -print -o ! -path . -prune |\
    sed 's/^..//' |\
    xargs "$@" # defaults to echo if no command given
}

globwrap 'part-*' | ...
globwrap "$OUTPUT"'.part-*' | ...
globwrap "$OUTPUT"'.part-*' rm

Одинарные кавычки предотвращают расширение оболочки глобусом мы переходим к find.

sed убирает ./, который в противном случае был бы добавлен к каждому имени файла.

Обратите внимание, что исходные ls и find больше не требуется в первых двух случаях.

...