У меня в скрипте оболочки есть следующее:
for file in $local_dir/myfile.log.*;
do
file_name=$(basename $file);
server_name=$(echo $file_name | cut -f 3 -d '.');
file_location=$(echo $file);
mv $file_location $local_dir/in_progress1.log
mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"
rm $local_dir/in_progress1.log
done
По сути, он получает все файлы в каталоге, соответствующие критериям, извлекает имя сервера из имени файла, а затем передает его в скрипт MySQL для обработки.
Мне интересно, что если у меня есть 10 файлов, выполнение каждого из которых занимает 60 секунд, и через 5 минут я запускаю второй экземпляр скрипта оболочки:
- а) будет ли второй скрипт по-прежнему видеть файлы, которые не были обработаны?
- б) вызовет ли это проблемы в первую очередь, если удалит файлы
или я смогу запускать их параллельно без проблем?
решение1
Можно предположить, что "60 секунд" (и даже "5 минут") — это просто хорошая оценка, и что есть риск, что первая партия все еще выполняется, когда начинается вторая партия. Если вы хотите разделить партии (и если нет никаких проблем, кроме файлов журнала в случайном наложении), лучшим подходом будет сделать номер партии частью соглашения об именовании файлов в процессе.
Что-то вроде этого:
[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch
перед циклом for, а затем в начале цикла проверьте, что ваш шаблон соответствует реальному файлу
[[ -f "$file" ]] || continue
и используйте номер партии в имени файла:
mv $file_location $local_dir/in_progress$batch.log
и вперед. Это снижает риск столкновения.
решение2
Выше есть ответ, который предлагает несколько хороших решений проблемы, но я подумал, что стоит дать небольшое пояснение по этому поводу.почемув чем проблема.
В большинстве случаев: пока ваши переименованные файлы журналов (те, которые находятся в процессе) не соответствуют критериям, вывероятнобезопасно запускать это сминимальныйриск. Хотя вы все равно получите некоторые ошибки...
Ваш список файлов генерируется при запуске скрипта. Так что в итоге произойдет следующее:
Script A
получает список 10 files
. Начинает обработку, 5 files
в (осталось 5) script B
получает список 5 remaining files
, начинает обработку. Script a
затем переходит к обработке следующего файла в своем списке (что то же самое, что файл script B
начал обрабатывать) он выдаст ошибку, потому что файл был переименован. Так что с обработкой ошибок это теоретически может перейти к следующему в своем списке и функционировать без проблем. Но, очевидно, ВСЕГДА есть вероятность, что звезды сойдутся, но скрипты попадут на один и тот же файл в одно и то же время, и произойдет что-то неожиданное. Взвешивайте этот риск как хотите.
Потенциально более элегантным решением было бы преобразовать это в python
скрипт и изучить его parallel for loops
, что позволило бы создать один цикл for и выполнять его параллельно, позволяя одному скрипту выполнять работу двух или более.
решение3
Другой способ сделать это — реализовать в вашем скрипте простую пакетную очередь.
В начале скрипта можно сделать что-то вроде этого:
mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"
# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"
# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"
rm -f "$BATCHTMP"
# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done
rm -f "$MYBATCH"
Конечно, это лишь краткий обзор того, что необходимо сделать.
Кстати, это также можно сделать в скрипте-оболочке, который ничего не делает, кроме как генерирует пакетный файл, а затем запускает основной скрипт.