← Весь справочник

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 и -w flock ждёт бесконечно. В cron это значит, что зависшие задачи будут копиться. Для cron почти всегда нужен -n.
  • Выбирай место для файла. /tmp очищается при перезагрузке и доступен всем пользователям — чужой процесс может занять твою блокировку. Для системных задач подходит /run/ или /var/lock/.

См. также

cron · background · redirection · timeout · exit-codes