У меня есть скрипт, который обрабатывает пакет из 20MiB CSV-файлов, опционально gzip-сжатый до примерно 4MiB. Существует много тысяч файлов, и каждая обработка по отдельности занимает около 30 секунд; читая либо несжатый файл, либо сжатый файл, а декомпрессию «почти мгновенно», что очень важно, чтобы процесс можно было распараллелить на уровне процесса. Действительно, это то, что делается с использованием сложного Ruby-конвейера. Тем не менее, я пытаюсь разбить код Ruby на более мелкие части, используя bash. Для контроля работы, я бы придумал эту функцию БашПодстановка замещения процесса Bash с управлением заданиями
wait_until_job_available() {
maximum_jobs=${MAXIMUM_JOBS}
[ $# -eq 0 ] || maximum_jobs="${1}"
exit_status=0
RUNNING_JOBS=($(jobs -p))
while [ ${maximum_jobs} -le ${#RUNNING_JOBS[@]} ] && [ 0 -eq "${exit_status}" ]
do
# `wait -n` requires bash 4.3 which is unfortunately not available on several recent RHEL-based Linux distributions such as Oracle Linux
wait -n
exit_status=$?
RUNNING_JOBS=($(jobs -p))
done
return ${exit_status}
}
Это позволяет мне позвонить wait_until_job_available
, с дополнительным минимальным количеством выполнения заданий разрешено (если опущен, то по умолчанию количество ядер доступны на машина), перед тем, как основываться на трубопроводе bash.
Так что я мог бы использовать его, как, например:
while read file
do
CAT_COMMAND=cat
# if input file is gzip-compressed, pipe zcat instead of cat
if [ "${INFILE: -3}" == ".gz" ]
then
CAT_COMMAND=zcat
fi
# wait for a job to become available
wait_until_job_available
# read the uncompressed file, write processed data to file.out
process_file -i <(${CAT_COMMAND} ${file}) -o ${file}.out &
# while searching for filesystem paths of type _f_ile
done < <(find ${search_path} -type f)
# wait for all background jobs to finish
wait
Как вы можете видеть, это должно найти все файлы в search_path
и передать команде process_file
. При этом я использую подстановку процессов либо в файл cat, либо при распаковке файла на лету; имя входного файла заменяется процессом, который будет выдавать содержимое несжатого файла, а выходной файл - это исходное имя файла с добавлением «.out». Invokeation process_file
получает справочную информацию и отправляется на управление заданиями. Выглядит денди, да?
За исключением того, что я заметил, что некоторые файлы не совсем корректно обрабатываются.
Я заметил, что файл, который, как считается, обрабатывается process_file
, всегда сообщается как /dev/fd/63
, даже для отдельных одновременных экземпляров process_file
. С другой стороны, когда я отдельно копирую или распаковываю файл на временный и передаю имя временного файла на process_file
, выполнение выполняется нормально, и все файлы выглядят корректно обработанными.
Я хотел избежать создания временного файла, особенно касательно касания диска (производительности) и необходимости очистки (удаления) временного файла после обработки; эта проблема мешает этому. Так что мне любопытно, есть ли какое-то состояние гонки на имя псевдофайла для замещенного технологического конвейера? Или есть что-то о замещении процесса или контроле работы, которое я, кажется, неправильно понимаю?
Для справки, я использую Ubuntu Server 14.04, Linux 3.19.0-59 Bash 4.3.11 GZIP 1,6
С моей точки зрения, файловый дескриптор создается Bash, хотя – inetknght