Тем не менее, я хочу обсудить несколько различных неработающих и полуработоспособных подходов ps
и их предостережения, поскольку я продолжаю видеть, как люди их используют.
Этот ответ действительно является ответом «Почему бы не использовать ps
и grep
обрабатывать блокировку в оболочке?
Сломанный подход № 1
Во-первых, подход, приведенный в другом ответе, который имеет несколько положительных отзывов, несмотря на тот факт, что он не работает (и не мог) и явно никогда не проверялся:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
Давайте исправим синтаксические ошибки и неверные ps
аргументы и получим:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
Этот скрипт всегда будет выходить из 6 каждый раз, независимо от того, как вы его запустите.
Если вы запустите его ./myscript
, то ps
будет просто вывод 12345 -bash
, который не соответствует требуемой строке 12345 bash ./myscript
, поэтому произойдет сбой.
Если вы запустите его bash myscript
, все станет интереснее. Процесс bash разветвляет запуск конвейера, а дочерняя оболочка запускает ps
и grep
. И исходная оболочка, и дочерняя оболочка будут отображаться в ps
выводе, что-то вроде этого:
25793 bash myscript
25795 bash myscript
Это не ожидаемый результат $$ bash $0
, поэтому ваш скрипт завершится.
Неправильный подход № 2
Теперь, честно говоря, для пользователя, который написал неправильный подход № 1, я сам сделал нечто подобное, когда впервые попробовал это:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
Это почти работает. Но сам факт разветвления, чтобы запустить трубу, отбрасывает это. Так что и этот всегда будет выходить.
Ненадежный подход № 3
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
Эта версия устраняет проблему разветвления конвейера в подходе # 2, сначала получая все PID, которые имеют текущий сценарий в своих аргументах командной строки, а затем отфильтровывая этот пидлист отдельно, чтобы пропустить PID текущего сценария.
Это может сработать ... при условии, что ни у какого другого процесса нет командной строки, совпадающей с $0
, и при условии, что скрипт всегда вызывается одинаково (например, если он вызывается с относительным путем, а затем с абсолютным путем, последний экземпляр не заметит первый ).
Ненадежный подход № 4
Так что, если мы пропустим проверку полной командной строки, так как это может не указывать на фактическую работу скрипта, и lsof
вместо этого проверим, чтобы найти все процессы, у которых этот скрипт открыт?
Ну да, такой подход на самом деле не так уж и плох
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
Конечно, если запущена копия скрипта, то новый экземпляр запустится просто отлично, и у вас будет две запущенные копии .
Или, если работающий скрипт изменен (например, с помощью Vim или с помощью a git checkout
), тогда «новая» версия скрипта запустится без проблем, так как и Vim, и git checkout
результатом будет новый файл (новый inode) вместо Старый.
Однако, если скрипт никогда не изменяется и никогда не копируется, то эта версия довольно хороша. Условия гонки отсутствуют, потому что файл сценария должен быть открыт до того, как будет достигнута проверка.
Все еще могут быть ложные срабатывания, если у другого процесса открыт файл скрипта, но учтите, что даже если он открыт для редактирования в Vim, vim на самом деле не держит файл скрипта открытым, поэтому не приведет к ложным срабатываниям.
Но помните, не используйте этот подход, если скрипт может быть отредактирован или скопирован, так как вы получите ложные негативы, т.е. несколько экземпляров, запущенных одновременно - поэтому тот факт, что редактирование с помощью Vim не дает ложных срабатываний, не должен иметь значения тебе. Я упоминаю это, хотя, потому что подход # 3 делает ложные срабатывания (т.е. не запускается) , если у вас есть сценарий открытого с Vim.
Так что же делать тогда?
Топ проголосовали ответ на этот вопрос дает хороший твердый подход.
Возможно, вы можете написать лучшую ... но если вы не понимаете всех проблем и предостережений со всеми вышеупомянутыми подходами, вы вряд ли напишете метод блокировки, который позволит избежать их всех.
kill
; и кажется хорошей практикой хранить собственный файл в файле замка, а не просто прикасаться к нему.