flock
Блокировки файлов из shell — не дать двум копиям скрипта или cron-задачи работать одновременно
flock берёт блокировку на файл (системный вызов flock(2)) и запускает команду, пока блокировка удерживается. Если блокировку уже держит другой процесс, flock ждёт или сразу сдаётся. Главное применение — защита от параллельного запуска: cron-задача, которая не успела закончиться к следующему запуску, деплой-скрипт, обработчик очереди. Блокировка снимается сама, когда процесс-владелец завершается, даже аварийно, — в отличие от «pid-файлов», которые остаются после падения. Утилита входит в util-linux.
Синтаксис
flock [опции] файл команда [аргументы…] # заблокировать файл на время команды
flock [опции] файл -c 'строка команд' # команда через sh -c
flock [опции] номер_fd # заблокировать уже открытый дескриптор
Файл блокировки создаётся, если его нет (каталог должен существовать). Содержимое файла не важно — блокировка привязана к самому файлу, а не к данным.
Опции
| Опция | Что делает |
|---|---|
-x, --exclusive | эксклюзивная блокировка — только один владелец (по умолчанию) |
-s, --shared | разделяемая блокировка — несколько «читателей» сразу, но не вместе с -x |
-n, --nonblock | не ждать: если занято, сразу выйти с кодом 1 |
-w сек, --timeout | ждать не дольше N секунд (можно дробное), потом выйти с кодом 1 |
-E код | код выхода при занятой блокировке или таймауте вместо 1 |
-u, --unlock | снять блокировку с дескриптора (обычно не нужно — снимается при закрытии) |
-o, --close | закрыть дескриптор блокировки перед запуском команды: её потомки не унаследуют блокировку |
-c 'команды' | выполнить строку через sh -c |
-F, --no-fork | не создавать дочерний процесс, заменить flock командой |
--verbose | сообщить, сколько ждали блокировку и что запускается |
Коды возврата
| Код | Когда |
|---|---|
| код команды | блокировка получена, команда выполнилась |
1 | блокировка занята (-n) или истёк таймаут (-w); или своё значение из -E |
66 | не удалось открыть файл блокировки (например, нет каталога) |
Две формы использования
| Форма | Пример | Когда |
|---|---|---|
| обёртка вокруг команды | flock -n /run/job.lock /opt/job.sh | в crontab, когда скрипт менять не хочется |
| дескриптор внутри скрипта | exec 9>/run/job.lock; flock -n 9 || exit 1 | скрипт сам защищает себя; блокировка живёт до конца скрипта |
Примеры
# cron: запускать раз в минуту, но не накладывать запуски друг на друга
# * * * * * flock -n /run/backup.lock /opt/backup.sh
# подождать освобождения не больше 30 секунд
flock -w 30 /run/deploy.lock ./deploy.sh || echo "деплой уже идёт" >&2
# отличить «занято» от ошибки самой команды
flock -n -E 75 /run/job.lock ./job.sh
[ $? -eq 75 ] && echo "уже запущено"
# скрипт, который защищает сам себя: блокировка на всё время работы
cat > /tmp/job.sh <<'EOF'
#!/bin/bash
exec 9>/tmp/job.lock
if ! flock -n 9; then
echo "уже запущена другая копия" >&2
exit 1
fi
echo "работаю, PID $$"
sleep 5
EOF
chmod +x /tmp/job.sh
/tmp/job.sh & sleep 0.5; /tmp/job.sh # вторая копия сразу выходит
# защитить только часть скрипта: блок в подоболочке
(
flock -w 10 9 || exit 1
echo "запись в общий файл" >> /tmp/shared.log
) 9>/tmp/shared.lock
# разделяемая блокировка для читателей, эксклюзивная — для писателя
flock -s /tmp/data.lock cat /tmp/data.txt
flock -x /tmp/data.lock sh -c 'date > /tmp/data.txt'
# несколько команд под одной блокировкой
flock /tmp/a.lock -c 'echo start; sleep 1; echo end'
Подводные камни
- Порядок аргументов с
-c. Сначала файл, потом-c:flock файл -c '…'. Записьflock -c '…' файл— ошибкаinvalid option -- 'c'. - Блокировку наследуют фоновые потомки. Если скрипт с
exec 9>файлзапустил что-то в фоне (сервис &) и завершился, дескриптор 9 остаётся у потомка, и блокировка не снимается. Закрывай дескриптор для фоновых процессов:команда 9>&- &; в форме-обёртке используй-o. - Не удаляй файл блокировки. Если удалить файл, пока его держит процесс, следующий запуск создаст новый файл с другим inode и спокойно возьмёт «свою» блокировку — защита пропадает. Файл просто оставляй.
exec 9>файлобрезает файл. Если в файле блокировки что-то хранится, открывай его на дозапись или чтение:exec 9>>файлилиexec 9<файл.- Блокировки — рекомендательные. Они работают, только если все участники используют
flockна одном и том же файле. Обычныйcatилиecho >блокировку не проверяет. - Без
-nи-wflockждёт бесконечно. В cron это значит, что зависшие задачи будут копиться. Для cron почти всегда нужен-n. - Выбирай место для файла.
/tmpочищается при перезагрузке и доступен всем пользователям — чужой процесс может занять твою блокировку. Для системных задач подходит/run/или/var/lock/.
См. также
cron · background · redirection · timeout · exit-codes