Для фактичного використання слід скористатись голосовою відповіддю .
Однак я хочу обговорити деякі різні зламані та напівзапрацьовані підходи із використанням 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
редагуванні; і, здається, є хорошою практикою зберігати власний pid у файлі блокування, а не просто торкатися його.