@ jw013 спасибо! Так что, может быть, что-то вроде ln -s my.pid .lockэтого потребует блокировки (после echo $$ > my.pid) и при ошибке может проверить, действительно ли сохраненный в PID .lockактивный экземпляр скрипта
Тобиас Кинцлер,
Ответы:
18
Почти как ответ nsg: использовать каталог блокировки . Создание каталогов атомарно под Linux и Unix и * BSD и многими другими ОС.
if mkdir $LOCKDIR
then# Do important, exclusive stuffif rmdir $LOCKDIR
then
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# Handle error condition...fi
Вы можете поместить PID блокирующего sh в файл в каталоге lock для целей отладки, но не попадайтесь в ловушку, думая, что вы можете проверить этот PID, чтобы убедиться, что процесс блокировки все еще выполняется. Многие условия гонки лежат на этом пути.
Я хотел бы рассмотреть возможность использования сохраненного PID, чтобы проверить, жив ли экземпляр блокировки. Тем не менее, вот заявление, mkdirкоторое не является атомарным в NFS (что не так для меня, но я думаю, что следует упомянуть об этом, если это правда)
Тобиас Кинцлер,
Да, во что бы то ни стало, используйте сохраненный PID, чтобы увидеть, выполняется ли еще процесс блокировки, но не пытайтесь делать что-либо кроме записи сообщения. Проверка сохраненного pid, создание нового файла PID и т. Д. Оставляет большое окно для гонок.
Брюс Эдигер
Хорошо, как сказал Ihunath , lockdir, скорее всего, будет в том, /tmpгде обычно нет общего доступа к NFS, так что все должно быть в порядке.
Тобиас Кинцлер
Я бы использовал, rm -rfчтобы удалить каталог блокировки. rmdirпотерпит неудачу, если кому-то (не обязательно вам) удалось добавить файл в каталог.
Чепнер
18
Чтобы добавить ответ Брюса Эдигера и вдохновленный этим ответом , вы также должны добавить больше умов в очистку, чтобы предотвратить завершение сценария:
#Remove the lock directoryfunction cleanup {if rmdir $LOCKDIR;then
echo "Finished"else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1fi}if mkdir $LOCKDIR;then#Ensure that if we "grabbed a lock", we release it#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"# Processing starts hereelse
echo "Could not create lock directory '$LOCKDIR'"
exit 1fi
Я использовал это условие в течение недели, и в двух случаях это не помешало запуску нового процесса. Я понял, в чем проблема - новый pid является подстрокой старого и скрыт grep -v $$. реальные примеры: старый - 14532, новый - 1453, старый - 28858, новый - 858.
Naktibalda
Я исправил это, изменив grep -v $$наgrep -v "^${$} "
Naktibalda
@Naktibalda хороший улов, спасибо! Вы также можете исправить это с помощью grep -wv "^$$"(см. Редактирование).
Terdon
Спасибо за это обновление. Мой образец иногда терпел неудачу, потому что более короткие pids были оставлены дополненными пробелами.
Naktibalda
4
Я бы использовал файл блокировки, как упоминал Марко
(Я думаю, что вы забыли создать файл блокировки) Как насчет условий гонки ?
Тобиас Кинцлер,
ops :) Да, условия гонки в моем примере - проблема, я обычно пишу почасовые или ежедневные задания cron, а условия гонки редки.
НСГ
Они не должны быть актуальны и в моем случае, но об этом нужно помнить. Может быть, использование lsof $0не плохо, либо?
Тобиас Кинцлер,
Вы можете уменьшить состояние гонки, написав свой $$файл блокировки. Затем sleepна короткий промежуток времени и прочитайте его обратно. Если PID остается вашим, вы успешно получили блокировку. Не требует абсолютно никаких дополнительных инструментов.
manatwork
1
Я никогда не использовал lsof для этой цели, мне это должно работать. Обратите внимание, что в моей системе lsof работает очень медленно (1-2 секунды), и, скорее всего, для гонок достаточно времени.
НСГ
3
Если вы хотите убедиться, что работает только один экземпляр вашего скрипта, взгляните на:
В противном случае вы можете проверить psили вызвать lsof <full-path-of-your-script>, так как я бы не назвал их дополнительными инструментами.
Дополнение :
на самом деле я думал сделать это так:
for LINE in`lsof -c <your_script> -F p`;doif[ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running"1>&2
exit 1;fidone
это гарантирует, что только процесс с самым низким pidпродолжит работу, даже если вы выполняете <your_script>одновременно несколько форков и исполняете их .
Спасибо за ссылку, но не могли бы вы включить основные части в свой ответ? В SE распространена политика предотвращения гниения ссылок ... Но чего-то подобного на [[(lsof $0 | wc -l) > 2]] && exitсамом деле может быть достаточно, или это также подвержено условиям гонки?
Тобиас Кинцлер,
Вы правы, основная часть моего ответа пропущена, и только публикация ссылок довольно хромая. Я добавил свое собственное предложение в ответ.
user1146332
3
Еще один способ убедиться, что запускается один экземпляр скрипта bash:
#!/bin/bash# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running"&& exit 1...
pidof -o %PPID -x $0 получает PID существующего сценария, если он уже запущен, или завершается с кодом ошибки 1, если не запущен другой сценарий
Это происходит из раздела примеров man flock, который дополнительно объясняет:
Это полезный шаблонный код для сценариев оболочки. Поместите его в верхнюю часть сценария оболочки, который вы хотите заблокировать, и он автоматически заблокируется при первом запуске. Если env var $ FLOCKER не установлен для запускаемого сценария оболочки, тогда выполните flock и получите эксклюзивную неблокирующую блокировку (используя сам сценарий в качестве файла блокировки), прежде чем переопределить себя с правильными аргументами. Он также устанавливает FLOCKER env var в правильное значение, чтобы он больше не запускался.
Вопросы для рассмотрения:
Требуется flock, пример сценария завершается с ошибкой, если он не может быть найден
Это модифицированная версия Ответа Ансельмо . Идея состоит в том, чтобы создать дескриптор файла только для чтения, используя сам скрипт bash, и использовать его flockдля обработки блокировки.
SCRIPT=`realpath $0`# get absolute path to the script itself
exec 6<"$SCRIPT"# open bash script using file descriptor 6
flock -n 6||{ echo "ERROR: script is already running"&& exit 1;}# lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
Основное отличие от всех остальных ответов заключается в том, что этот код не изменяет файловую систему, использует очень низкую площадь и не требует никакой очистки, поскольку дескриптор файла закрывается, как только сценарий завершается независимо от состояния выхода. Таким образом, не имеет значения, если скрипт потерпит неудачу или удастся.
Вы должны всегда заключать в кавычки все ссылки на переменные оболочки, если у вас нет веских причин не делать этого, и вы уверены, что знаете, что делаете. Так что ты должен делать exec 6< "$SCRIPT".
Скотт
@ Скотт Я изменил код в соответствии с вашими предложениями. Большое спасибо.
Джон Доу
1
Я использую cksum, чтобы проверить, что мой скрипт действительно запускает один экземпляр, даже я меняю имя файла и путь к файлу .
Я не использую trap & lock file, потому что, если мой сервер внезапно отключается, мне нужно вручную удалить файл блокировки после того, как сервер заработает.
Примечание: #! / Bin / bash в первой строке требуется для grep ps
Или вы можете жестко запрограммировать cksum внутри своего скрипта, чтобы вы не беспокоились, если хотите изменить имя файла, путь или содержимое вашего скрипта .
Пожалуйста, объясните точно, как жесткая кодировка контрольной суммы является хорошей идеей.
Скотт
не жесткая контрольная сумма, он только создает ключ идентификации вашего скрипта, когда другой экземпляр будет запущен, он будет проверять другой процесс сценария оболочки и сначала отследит файл, если ваш ключ идентификации находится в этом файле, поэтому он означает, что ваш экземпляр уже запущен.
арпутра
ХОРОШО; пожалуйста, отредактируйте свой ответ, чтобы объяснить это. И, в будущем, не публикуйте несколько блоков кода длиной 30 строк, которые выглядят так, как будто они (почти) идентичны, не говоря и не объясняя, чем они отличаются. И не говорите что-то вроде «вы можете жестко закодировать [sic] cksum внутри вашего скрипта», и не продолжайте использовать имена переменных mysumи fsum, когда вы больше не говорите о контрольной сумме.
Скотт
Выглядит интересно, спасибо! И добро пожаловать в unix.stackexchange :)
ln -s my.pid .lock
этого потребует блокировки (послеecho $$ > my.pid
) и при ошибке может проверить, действительно ли сохраненный в PID.lock
активный экземпляр скриптаОтветы:
Почти как ответ nsg: использовать каталог блокировки . Создание каталогов атомарно под Linux и Unix и * BSD и многими другими ОС.
Вы можете поместить PID блокирующего sh в файл в каталоге lock для целей отладки, но не попадайтесь в ловушку, думая, что вы можете проверить этот PID, чтобы убедиться, что процесс блокировки все еще выполняется. Многие условия гонки лежат на этом пути.
источник
mkdir
которое не является атомарным в NFS (что не так для меня, но я думаю, что следует упомянуть об этом, если это правда)/tmp
где обычно нет общего доступа к NFS, так что все должно быть в порядке.rm -rf
чтобы удалить каталог блокировки.rmdir
потерпит неудачу, если кому-то (не обязательно вам) удалось добавить файл в каталог.Чтобы добавить ответ Брюса Эдигера и вдохновленный этим ответом , вы также должны добавить больше умов в очистку, чтобы предотвратить завершение сценария:
источник
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
.Это может быть слишком упрощенно, пожалуйста, поправьте меня, если я ошибаюсь. Разве это не
ps
достаточно просто ?источник
grep -v $$
. реальные примеры: старый - 14532, новый - 1453, старый - 28858, новый - 858.grep -v $$
наgrep -v "^${$} "
grep -wv "^$$"
(см. Редактирование).Я бы использовал файл блокировки, как упоминал Марко
источник
lsof $0
не плохо, либо?$$
файл блокировки. Затемsleep
на короткий промежуток времени и прочитайте его обратно. Если PID остается вашим, вы успешно получили блокировку. Не требует абсолютно никаких дополнительных инструментов.Если вы хотите убедиться, что работает только один экземпляр вашего скрипта, взгляните на:
Блокировка вашего скрипта (против параллельного запуска)
В противном случае вы можете проверить
ps
или вызватьlsof <full-path-of-your-script>
, так как я бы не назвал их дополнительными инструментами.Дополнение :
на самом деле я думал сделать это так:
это гарантирует, что только процесс с самым низким
pid
продолжит работу, даже если вы выполняете<your_script>
одновременно несколько форков и исполняете их .источник
[[(lsof $0 | wc -l) > 2]] && exit
самом деле может быть достаточно, или это также подвержено условиям гонки?Еще один способ убедиться, что запускается один экземпляр скрипта bash:
pidof -o %PPID -x $0
получает PID существующего сценария, если он уже запущен, или завершается с кодом ошибки 1, если не запущен другой сценарийисточник
Хотя вы просили решение без дополнительных инструментов, это мой любимый способ использования
flock
:Это происходит из раздела примеров
man flock
, который дополнительно объясняет:Вопросы для рассмотрения:
flock
, пример сценария завершается с ошибкой, если он не может быть найденСм. Также /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
источник
Это модифицированная версия Ответа Ансельмо . Идея состоит в том, чтобы создать дескриптор файла только для чтения, используя сам скрипт bash, и использовать его
flock
для обработки блокировки.Основное отличие от всех остальных ответов заключается в том, что этот код не изменяет файловую систему, использует очень низкую площадь и не требует никакой очистки, поскольку дескриптор файла закрывается, как только сценарий завершается независимо от состояния выхода. Таким образом, не имеет значения, если скрипт потерпит неудачу или удастся.
источник
exec 6< "$SCRIPT"
.Я использую cksum, чтобы проверить, что мой скрипт действительно запускает один экземпляр, даже я меняю имя файла и путь к файлу .
Я не использую trap & lock file, потому что, если мой сервер внезапно отключается, мне нужно вручную удалить файл блокировки после того, как сервер заработает.
Примечание: #! / Bin / bash в первой строке требуется для grep ps
Или вы можете жестко запрограммировать cksum внутри своего скрипта, чтобы вы не беспокоились, если хотите изменить имя файла, путь или содержимое вашего скрипта .
источник
mysum
иfsum
, когда вы больше не говорите о контрольной сумме.Вы можете использовать это: https://github.com/sayanarijit/pidlock
источник
Мой код тебе
На основании
man flock
только улучшения:executing
Где я положил здесь
sleep 10
, вы можете поместить все основные сценарий.источник